תמיכה בחלונות במחשב

התכונה 'ממשק מחשב' מאפשרת למשתמשים להפעיל כמה אפליקציות בו-זמנית בחלונות אפליקציות שניתן לשנות את הגודל שלהם, וכך ליהנות מחוויה מגוונת שדומה לשימוש במחשב.

באיור 1 אפשר לראות את ארגון המסך עם ממשק מחשב מופעל. כדאי לשים לב:

  • המשתמשים יכולים להפעיל כמה אפליקציות זו לצד זו בו-זמנית.
  • סרגל האפליקציות נמצא במיקום קבוע בתחתית המסך, ומוצגות בו האפליקציות הפועלות. המשתמשים יכולים להצמיד אפליקציות כדי לגשת אליהן במהירות.
  • פס כותרת חדש שניתן להתאמה אישית מעטר את החלק העליון של כל חלון עם אמצעי בקרה כמו הקטנה והגדלה.
איור 1. ממשק מחשב בטאבלט.

כברירת מחדל, אפליקציות נפתחות במסך מלא בטאבלטים עם Android. כדי להפעיל אפליקציה במצב 'ממשק מחשב', לוחצים לחיצה ארוכה על נקודת האחיזה של החלון בחלק העליון של המסך וגוררים את נקודת האחיזה בתוך ממשק המשתמש, כמו שרואים באיור 2.

כשאפליקציה פתוחה בממשק מחשב, גם אפליקציות אחרות נפתחות בחלונות בממשק מחשב.

איור 2. לוחצים לחיצה ארוכה על נקודת האחיזה של חלון האפליקציה וגוררים אותה כדי להפעיל את המצב "ממשק מחשב".

משתמשים יכולים גם להפעיל את חלונות שולחן העבודה מהתפריט שמופיע מתחת לידית החלון כשמקישים או לוחצים על הידית או משתמשים במקשי הקיצור מקש Meta (Windows,‏ Command או חיפוש) + Ctrl + חץ למטה.

משתמשים יוצאים מממשק המחשב על ידי סגירת כל החלונות הפעילים או על ידי אחיזה בנקודת האחיזה של החלון בחלק העליון של חלון בממשק המחשב וגרירת האפליקציה לחלק העליון של המסך. מקשי הקיצור Meta + H גם יוצאים מממשק שולחן העבודה ומריצים שוב את האפליקציות במסך מלא.

כדי לחזור לממשק מחשב, מקישים או לוחצים על המשבצת של שולחן העבודה במסך 'האחרונים'.

שינוי גודל ומצב תאימות

בממשק מחשב, אפליקציות עם כיוון נעול ניתנות לשינוי גודל באופן חופשי. כלומר, גם אם פעילות נעולה לאוריינטציה אנכית, המשתמשים עדיין יכולים לשנות את גודל האפליקציה לחלון עם אוריינטציה אופקית.

איור 3. שינוי הגודל של חלון של אפליקציה שמוגבלת לפורטרט לרוחב.

ממשק המשתמש של אפליקציות שהוגדרו כלא ניתנות לשינוי גודל (כלומר, resizeableActivity = false) עובר שינוי קנה מידה, אבל יחס הגובה-רוחב נשאר זהה.

איור 4. ממשק המשתמש של אפליקציה שלא ניתן לשנות את הגודל שלה משתנה בהתאם לשינוי הגודל של החלון.

אפליקציות מצלמה שנועלות את הכיוון או שמוצהר לגביהן שהן לא ניתנות לשינוי גודל, מקבלות טיפול מיוחד בעינית המצלמה שלהן: אפשר לשנות את הגודל של החלון, אבל יחס הגובה-רוחב של העינית נשאר זהה. אם האפליקציות מניחות שהן תמיד פועלות במצב לאורך או לרוחב, הן מקודדות או מניחות הנחות שמובילות לחישובים שגויים של כיוון התצוגה המקדימה או של התמונה שצולמה, או של יחס הגובה-רוחב שלהן. התוצאה היא תמונות מתוחות, צדדיות או הפוכות.

עד שהאפליקציות יהיו מוכנות להטמעה של עיניות מצלמה רספונסיביות לחלוטין, הטיפול המיוחד מספק חוויית משתמש בסיסית יותר שמצמצמת את ההשפעות שעלולות להיגרם מהנחות שגויות.

מידע נוסף על מצב תאימות לאפליקציות מצלמה זמין במאמר מצב תאימות של מכשירים.

איור 5. יחס הגובה-רוחב של העינית של המצלמה נשמר כשמשנים את גודל החלון.

הגדרת שוליים פנימיים בכותרת בהתאמה אישית

לכל האפליקציות שפועלות במצב ממשק מחשב יש פס עליון, גם במצב מסך מלא. אתם יכולים להתאים אישית את הסרגל הזה כדי למנוע הסתרה של התוכן באפליקציה, וכדי להוסיף רכיבי ממשק משתמש מותאמים אישית ישירות למרחב של הכותרת.

‫Chrome לפני ואחרי הטמעה של כותרות מותאמות אישית.
איור 6. ‫Chrome לפני ואחרי הטמעה של כותרות מותאמות אישית.

הטמעה

כדי לצייר תוכן בהתאמה אישית בסרגל הכותרת, קודם צריך להגדיר את הרקע של סרגל הכותרת כשקוף. כדי לעשות את זה, משתמשים בדגל APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND עם WindowInsetsController.

window.insetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
)

אחרי שסרגל הכותרת הופך לשקוף, אפשר לעצב את אזור הכותרת כך שיתאים לעיצוב של האפליקציה. משתמשים ב-WindowInsets.isCaptionBarVisible כדי לזהות אם הסרגל קיים ולהחיל את הגובה או הריווח המתאימים על הפריסה.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CaptionBar() {
    if (WindowInsets.isCaptionBarVisible) {
        Row(
            modifier = Modifier
                .windowInsetsTopHeight(WindowInsets.captionBar)
                .fillMaxWidth()
                .background(if (isSystemInDarkTheme()) Color.White else Color.Black),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Caption Bar Title",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(4.dp)
            )
        }
    }
}

  • setSystemBarsAppearance(appearance,mask): הגדרת הסגנון החזותי של סרגלי המערכת. הפרמטר הראשון מגדיר את דגלי המראה של היעד, והפרמטר השני משמש כמסכה כדי לקבוע אילו דגלים ספציפיים ישונו.

  • windowInsetsTopHeight(): מגדיר אוטומטית את הגובה של רכיב ה-Composable כך שיתאים לסרגל הכותרת של המערכת, ועוזר לרקע המותאם אישית למלא את אזור הכיתוב בלי לקודד ערכי פיקסלים.

  • WindowInsets.captionBar: מספק את המידות של אמצעי הבקרה של חלונות במחשב (סגירה, הגדלה וכו'), ומאפשר לממשק המשתמש שלכם לשנות את הגודל או להסתיר את עצמו באופן אוטומטי כשנכנסים לחלונות במחשב או יוצאים מהם.

מידע נוסף זמין במאמר בנושא שוליים פנימיים של חלונות. בנוסף לכותרת, אפשר להציג בסרגל הכיתוב רכיבי ממשק משתמש אחרים, כמו כרטיסיות – כמו ב-Google Chrome – סרגלי חיפוש או תמונות פרופיל.

ממשק משתמש

כדי להימנע מחפיפה בין ממשק המשתמש לבין כפתורי המערכת, ב-Android 15 יש את השיטה WindowInsets#getBoundingRects(). השיטה מחזירה רשימה של אובייקטים מסוג Rect שמייצגים אזורים שבהם יש רכיבי מערכת. כל השטח שנותר בסרגל הכיתוב הוא אזור גלוי בוודאות שבו אפשר להציב בבטחה תוכן מותאם אישית.

אפשר להחליף בין עיצוב בהיר לכהה של אלמנטים של כתוביות במערכת באמצעות APPEARANCE_LIGHT_CAPTION_BARS. כדי לגשת לתוספים מוטבעים, משתמשים ב-WindowInsets.Companion.captionBar() בכלי הכתיבה או ב-WindowInsets.Type.captionBar() ב-Views.

מידע נוסף זמין במאמר בנושא שוליים פנימיים של חלונות.

תמיכה במולטיטסקינג ובכמה מופעים במקביל

ריבוי משימות הוא הבסיס של התכונה 'ממשק מחשב', ואם תאפשרו למשתמשים להפעיל כמה מופעים של האפליקציה שלכם, הם יוכלו להיות הרבה יותר פרודוקטיביים.

החל מ-Android 15, אפשר להשתמש ב-PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. כשמגדירים את המאפיין הזה ב-AndroidManifest.xml, מציינים שממשק המשתמש של המערכת צריך לספק אפשרויות (כמו לחצן 'חלון חדש') להפעלת האפליקציה בכמה מופעים.

<application>
    <property
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</application>

הערה: בסביבות מרובות חלונות, כמו חלונות בשולחן העבודה, משימות חדשות נפתחות בחלון חדש. לכן, חשוב לבדוק את תהליך המשתמש בכל פעם שהאפליקציה מתחילה כמה משימות.

ניהול מופעים של אפליקציות באמצעות תנועות גרירה

במצב מרובה חלונות, משתמשים יכולים להתחיל מופע חדש של אפליקציה על ידי גרירת רכיב בממשק המשתמש (כמו כרטיסייה או מסמך) מחוץ לחלון של האפליקציה. המשתמשים יכולים גם להעביר רכיבים בין מופעים שונים של אותה אפליקציה.

איור 7. מתחילים מופע חדש של Chrome על ידי גרירת כרטיסייה מחוץ לחלון של שולחן העבודה.

העברת נתונים באמצעות גרירה ושחרור

כדי להגדיר רכיב composable כמקור לגרירה של כמה מופעים במקביל (כך שהמשתמשים יוכלו לגרור תוכן למופע אחר של האפליקציה או ליצור מופע חדש על ידי גרירת תוכן לאזור ריק במסך), צריך להשתמש במאפיין dragAndDropSource. בפונקציית ה-lambda, מחזירים את הערך DragAndDropTransferData, מעבירים את הערך ClipData שמכיל את הנתונים להעברה ודגלים להגדרת התנהגות של כמה מופעים במקביל.

ב-Android 15 יש שני דגלים מרכזיים להצגת חלונות בסגנון מחשב ולשימוש בכמה מופעים במקביל:

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: מציין שאפשר לבצע פעולת גרירה בין חלונות (לכמה מופעים של אותה אפליקציה). כשקוראים ל-startDragAndDrop() עם ההגדרה הזו, רק חלונות גלויים ששייכים לאותה אפליקציה יכולים להשתתף בפעולת הגרירה ולקבל את התוכן שנגרר.

Modifier.dragAndDropSource { _ ->
    DragAndDropTransferData(
        clipData = ClipData.newPlainText("label", "Your data"),
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION
    )
}

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: מאפשר למשתמשים להתחיל מופע חדש של האפליקציה על ידי גרירת התוכן והשלכתו לאזור ריק במסך, אם אף חלון אחר לא מטפל בהשלכה.
    • כשמשתמשים בדגל הזה, צריך לספק IntentSender באמצעות ClipData.Item.Builder#setIntentSender(). המערכת משתמשת בערך הזה כדי להפעיל את הפעילות החדשה אם מתרחשת השמטה שלא טופלה.

Modifier.dragAndDropSource { _ ->
    val intent = Intent.makeMainActivity(activity.componentName).apply {
        putExtra("EXTRA_ITEM_ID", itemId)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
                Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
    }

    val pendingIntent = PendingIntent.getActivity(
        activity, 0, intent, PendingIntent.FLAG_IMMUTABLE
    )

    val data = ClipData(
        "Item $itemId",
        arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT),
        ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build()
    )

    DragAndDropTransferData(
        clipData = data,
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or
                View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
    )
}

קבלת הנתונים שהועברו

כדי לאשר קבלת נתונים ממופע אחר, משתמשים בשינוי dragAndDropTarget. אם הנתונים מגיעים ממופע או מאפליקציה אחרים, צריך לבקש הרשאות באופן מפורש.

Modifier.dragAndDropTarget(
    shouldStartDragAndDrop = { event ->
        event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
    },
    target = object : DragAndDropTarget {
        override fun onDrop(event: DragAndDropEvent): Boolean {
            requestDragAndDropPermissions(activity, event.toAndroidDragEvent())
            val clipData = event.toAndroidDragEvent().clipData
            val item = clipData?.getItemAt(0)?.text
            if (item != null) {
                // Process the dropped text item here
            }
            return item != null
        }
    }
)

השלבים העיקריים:

  • מסנן: משתמשים ב-shouldStartDragAndDrop כדי לבדוק אם יש תמיכה בנתונים הנכנסים (סוג MIME).
  • הרשאות: התקשר אל requestDragAndDropPermissions(event) כדי לגשת לנתונים.
  • טיפול: חילוץ הנתונים בקריאה החוזרת onDrop.

אופטימיזציות נוספות

התאמה אישית של הפעלת אפליקציות ומעבר מאפליקציות במצב ממשק מחשב למסך מלא.

הגדרת גודל ומיקום כברירת מחדל

לא כל האפליקציות, גם אם אפשר לשנות את הגודל שלהן, צריכות חלון גדול כדי לספק ערך למשתמשים. אפשר להשתמש ב-method‏ ActivityOptions#setLaunchBounds() כדי לציין גודל ומיקום כברירת מחדל כשמפעילים פעילות.

כניסה למסך מלא מהחלל במחשב

אפליקציות יכולות לעבור למסך מלא באמצעות קריאה ל-Activity#requestFullScreenMode(). השיטה מציגה את האפליקציה במסך מלא ישירות מממשק מחשב.