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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

לכל האפליקציות שפועלות בחלונות בשולחן העבודה יש סרגל כותרת, גם במצב מסך מלא. מוודאים שסרגל הכותרת לא מסתיר את התוכן של האפליקציה. סרגל הכותרת הוא סוג של סרגל כתוביות מוזח: WindowInsets.Companion.captionBar(); בתצוגות, WindowInsets.Type.captionBar(), שהוא חלק מסרגלי המערכת.

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

אפשר גם להתאים אישית את סרגל הכותרת. ב-Android 15 הוסף סוג המראה APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND כדי להפוך את סרגל הכותרת לשקוף ולאפשר לאפליקציות לצייר תוכן מותאם אישית בתוך סרגל הכותרת.

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

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

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

רשימת האובייקטים Rect שמוחזרת על ידי ה-API מציינת אזורים במערכת שכדאי להימנע מהם. כל השטח שנותר (מחושב על ידי חיסור המלבנים מהשוליים של סרגל הכיתוב) הוא המקום שבו האפליקציה יכולה לצייר בלי לחפוף לרכיבי המערכת, ועם היכולת לקבל קלט.

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

כדי להגדיר מלבנים להחרגת מחוות מערכת עבור כותרת מותאמת אישית, מטמיעים את הקוד הבא בתצוגה או ב-Composable:

// In a custom View's onLayout or a similar lifecycle method
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    if (changed) {
        // Calculate the height of your custom header
        val customHeaderHeight = 100 // Replace with your actual header height in pixels

        // Create a Rect covering your custom header area
        val exclusionRect = Rect(0, 0, width, customHeaderHeight)

        // Set the exclusion rects for the system
        systemGestureExclusionRects = listOf(exclusionRect)
    }
}

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

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

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

אפשר להצהיר על PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI באפליקציה שלכם בAndroidManifest.xml בתג <activity>:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:resizeableActivity="true">
    <meta-data
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</activity>

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

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

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

ב-Android 15 נוספו שני דגלים להתאמה אישית של התנהגות הגרירה:

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

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: מציין שאפשר לגרור פעולה מעבר לגבולות החלון (למספר מופעים של אותה אפליקציה).

    כשקוראים ל-‎[startDragAndDrop()][20] עם ההגדרה הזו, רק חלונות גלויים ששייכים לאותה אפליקציה יכולים להשתתף בפעולת הגרירה ולקבל את התוכן שנגרר.

בדוגמה הבאה אפשר לראות איך משתמשים בדגלים האלה עם startDragAndDrop():

// Assuming 'view' is the View that initiates the drag
view.setOnLongClickListener {
    // Create an IntentSender for the activity you want to launch
    val launchIntent = Intent(view.context, NewInstanceActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(
        view.context,
        0,
        launchIntent,
        PendingIntent.FLAG_IMMUTABLE // Ensure the PendingIntent is immutable
    )

    // Build the ClipData.Item with the IntentSender
    val item = ClipData.Item.Builder()
        .setIntentSender(pendingIntent.intentSender)
        .build()

    // Create ClipData with a simple description and the item
    val dragData = ClipData(
        ClipDescription("New Instance Drag", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)),
        item
    )

    // Combine the drag flags
    val dragFlags = View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG or
                    View.DRAG_FLAG_GLOBAL_SAME_APPLICATION

    // Start the drag operation
    view.startDragAndDrop(
        dragData,                     // The ClipData to drag
        View.DragShadowBuilder(view), // A visual representation of the dragged item
        null,                         // Local state object (not used here)
        dragFlags                     // The drag flags
    )
    true // Indicate that the long click was consumed
}
איור 8. להעביר כרטיסייה בין שני מקרים של אפליקציית Chrome.

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

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

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

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

דוגמה להגדרת גבולות השקה לפעילות:

val options = ActivityOptions.makeBasic()

// Define the desired launch bounds (left, top, right, bottom in pixels)
val launchBounds = Rect(100, 100, 700, 600) // Example: 600x500 window at (100,100)

// Apply the launch bounds to the ActivityOptions
options.setLaunchBounds(launchBounds)

// Start the activity with the specified options
val intent = Intent(this, MyActivity::class.java)
startActivity(intent, options.toBundle())

מעבר למסך מלא מהחלל במחשב

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

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

// In an Activity
fun enterFullScreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 15 (U)
        requestFullScreenMode()
    }
}