מידע על WindowInsetsRulers

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

היתרונות של WindowInsetsRulers

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

חסרונות של WindowInsetsRulers

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

התאמת התוכן לשיטות של שינוי

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.fitInsider עם RectRuler שמקבל את הערך הפנימי ביותר של StatusBars ושל CaptionBar.

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

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

אפשר ליישר את התוכן לסרגלים בהתאמה אישית. לדוגמה, נניח שיש תוכן קומפוזבילי הורה שמטפל בצורה לא תקינה בשוליים הפנימיים, וזה גורם לבעיות בריווח בתוכן קומפוזבילי צאצא. אפשר לפתור את הבעיה הזו בדרכים אחרות, כולל באמצעות 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)
            }
        }
    }
}

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

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

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

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