لمحة عن WindowInsetsRulers

WindowInsets هي واجهة برمجة التطبيقات العادية في Jetpack Compose للتعامل مع مساحات الشاشة التي تحجبها واجهة مستخدم النظام جزئيًا أو كليًا. وتشمل هذه المناطق شريط الحالة وشريط التنقّل ولوحة المفاتيح على الشاشة. يمكنك بدلاً من ذلك تمرير WindowInsetsRulers محدّدة مسبقًا، مثل SafeDrawing، إلى Modifier.fitInside أو Modifier.fitOutside لمواءمة المحتوى مع أشرطة النظام وفتحة الشاشة أو إنشاء WindowInsetsRulers مخصّصة.

مزايا WindowInsetsRulers

  • تجنُّب تعقيد الاستهلاك: تعمل هذه الميزة خلال مرحلة تحديد موضع التصميم. وهذا يعني أنّه يتجاوز تمامًا سلسلة الاستهلاك المضمّنة ويمكنه دائمًا توفير المواضع الصحيحة والمطلقة لأشرطة النظام وفتحات الشاشة، بغض النظر عمّا فعلته تنسيقات العناصر الرئيسية. يساعد استخدام الطريقتَين 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 مخصّص

يمكنك محاذاة المحتوى مع مساطر مخصّصة. على سبيل المثال، لنفترض حالة استخدام يعالج فيها عنصر رئيسي قابل للإنشاء الحواف الداخلية بشكل غير صحيح، ما يؤدي إلى حدوث مشاكل في المساحة المتروكة في عنصر ثانوي لاحق. على الرغم من إمكانية حلّ هذه المشكلة بطرق أخرى، بما في ذلك باستخدام Modifier.fitInside، يمكنك أيضًا إنشاء مسطرة مخصّصة لمحاذاة العنصر الفرعي القابل للإنشاء بدقة بدون الحاجة إلى حلّ المشكلة في العنصر الرئيسي في المصدر، كما هو موضّح في المثال والفيديو التاليَين:

@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 لأنّ المساحة المتروكة لشريط التنقّل لم يتم استخدامها من خلال العنصر الرئيسي. يتم وضع الطفل في الموقع الصحيح في الصورة اليمنى باستخدام مسطرة مخصّصة، كما هو موضّح في عينة التعليمات البرمجية السابقة.

التأكّد من أنّ العناصر الأبوية مُقيَّدة

لاستخدام WindowInsetsRulers بأمان، يجب أن يقدّم الوالد قيودًا صالحة. يجب أن يكون للعناصر الرئيسية حجم محدّد، ولا يمكن أن تعتمد على حجم عنصر فرعي يستخدم WindowInsetsRulers. استخدِم fillMaxSize أو أدوات تعديل الحجم الأخرى في عناصر Composables الرئيسية.

وبالمثل، قد يؤدي وضع دالة مركّبة تستخدم WindowInsetsRulers داخل حاوية قابلة للتمرير مثل verticalScroll إلى حدوث سلوك غير متوقّع لأنّ الحاوية القابلة للتمرير توفّر قيودًا غير محدودة على الارتفاع، وهي غير متوافقة مع منطق أداة قياس المسافات.