מידע על WindowInsetsRulers

‫WindowInsets הוא ה-API הרגיל ב-Jetpack Compose לטיפול באזורים במסך שמוסתרים באופן חלקי או מלא על ידי ממשק המשתמש של המערכת. האזורים האלה כוללים את שורת הסטטוס, סרגל הניווט והמקלדת הווירטואלית. אפשר גם להעביר WindowInsetsRulers מוגדרים מראש כמו SafeDrawing אל Modifier.fitInside או Modifier.fitOutside כדי להתאים את התוכן לסרגלי המידע ולמגרעת במסך, או ליצור WindowInsetsRulers בהתאמה אישית.

היתרונות של WindowInsetsRulers

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

חסרונות של WindowInsetsRulers

  • אי אפשר להשתמש בו למדידה: מכיוון שהוא פועל בשלב מיקום המודעה, המידע על המיקום שהוא מספק לא זמין בשלב המדידה המוקדם יותר.

התאמת התוכן באמצעות שיטות של Modifier

Modifier.fitInside מאפשר לאפליקציות להתאים את התוכן לסרגלי המידע ולמגרעות במסך. אפשר להשתמש בו במקום WindowInsets. Modifier.fitOutside הוא בדרך כלל ההפך מ-Modifier.fitInside.

לדוגמה, כדי לוודא שתוכן האפליקציה לא מופיע בסרגלי המידע ובמגרעת במסך, אפשר להשתמש ב-fitInside(WindowInsetsRulers.safeDrawing.current).

@Composable
fun FitInsideDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc...
            .fitInside(WindowInsetsRulers.SafeDrawing.current)
    )
}

בטבלה הבאה מוצג איך תוכן האפליקציה ייראה עם סרגלים מוגדרים מראש עם Modifier.fitInside או Modifier.fitOutside.

סוג סרגל מוגדר מראש

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

לא רלוונטי

NavigationBars

SafeDrawing

לא רלוונטי (במקום זאת, צריך להשתמש ב-StatusBar,‏ CaptionBar,‏ NavigationBar)

StatusBar

כדי להשתמש ב-Modifier.fitInside וב-Modifier.fitOutside, צריך להגביל את הרכיבים הניתנים לשילוב. כלומר, צריך להגדיר משתנים כמו Modifier.size או Modifier.fillMaxSize.

חלק מהשליטים, כמו Modifier.fitOutside ב-SafeDrawing וב-SystemBars, מחזירים כמה שליטים. במקרה כזה, Android ממקמת את הרכיב הקומפוזבילי באמצעות סרגל אחד משמאל, מלמעלה, מימין ומלמטה.

הימנעות מ-IME באמצעות Modifier.fitInside

כדי לטפל ברכיבים תחתונים באמצעות IME עם Modifier.fitInside, מעבירים RectRuler שמקבל את הערך הפנימי ביותר של NavigationBar ו-Ime.

@Composable
fun FitInsideWithImeDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.NavigationBars.current,
                    WindowInsetsRulers.Ime.current
                )
            )
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier.align(Alignment.BottomStart).fillMaxWidth()
        )
    }
}

איך להימנע משורת הסטטוס ומשורת הכיתוב באמצעות Modifier.fitInside

באופן דומה, כדי לוודא שהרכיבים העליונים לא חופפים לשורת הסטטוס ולשורת הכותרת יחד עם Modifier.fitInside, מעבירים RectRuler שמקבל את הערך הפנימי ביותר של StatusBars ו-CaptionBar.

@Composable
fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.StatusBars.current,
                    WindowInsetsRulers.CaptionBar.current
                )
            )
    )
}

יצירת WindowInsetsRulers בהתאמה אישית

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

@Composable
fun WindowInsetsRulersDemo(modifier: Modifier) {
    Box(
        contentAlignment = BottomCenter,
        modifier = modifier
            .fillMaxSize()
            // The mistake that causes issues downstream, as .padding doesn't consume insets.
            // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars),
            // assume it's difficult to identify this issue to see how WindowInsetsRulers can help.
            .padding(WindowInsets.navigationBars.asPaddingValues())
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier
                // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child
                // Composable without having to fix the parent upstream.
                .alignToSafeDrawing()

            // .imePadding()
            // .fillMaxWidth()
        )
    }
}

fun Modifier.alignToSafeDrawing(): Modifier {
    return layout { measurable, constraints ->
        if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) {
            val placeable = measurable.measure(constraints)
            val width = placeable.width
            val height = placeable.height
            layout(width, height) {
                val bottom = WindowInsetsRulers.SafeDrawing.current.bottom
                    .current(0f).roundToInt() - height
                val right = WindowInsetsRulers.SafeDrawing.current.right
                    .current(0f).roundToInt()
                val left = WindowInsetsRulers.SafeDrawing.current.left
                    .current(0f).roundToInt()
                measurable.measure(Constraints.fixed(right - left, height))
                    .place(left, bottom)
            }
        } else {
            val placeable = measurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

בסרטון הבא מוצגת דוגמה לבעיה בשימוש ב-inset של IME שנגרמה על ידי רכיב הורה קודם בתמונה שמשמאל, ושימוש בסרגלים מותאמים אישית כדי לפתור את הבעיה בתמונה שמימין. ריפוד נוסף מוצג מתחת ל-TextField Composable כי הריפוד של סרגל הניווט לא נצרך על ידי ההורה. הילד ממוקם במיקום הנכון בתמונה השמאלית באמצעות סרגל מותאם אישית, כפי שמוצג בדוגמת הקוד הקודמת.

אימות שההורים מוגבלים

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

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