Tentang WindowInsetsRulers

WindowInsets adalah API standar di Jetpack Compose untuk menangani area layar yang sebagian atau seluruhnya terhalang oleh UI sistem. Area ini mencakup status bar, menu navigasi, dan keyboard di layar. Anda dapat secara alternatif meneruskan WindowInsetsRulers yang telah ditentukan seperti SafeDrawing ke Modifier.fitInside atau Modifier.fitOutside untuk menyelaraskan konten dengan kolom sistem dan potongan layar atau membuat WindowInsetsRulers kustom.

Keuntungan WindowInsetsRulers

  • Menghindari Kompleksitas Konsumsi: Beroperasi selama fase penempatan tata letak. Artinya, rantai konsumsi inset akan dilewati sepenuhnya dan selalu dapat memberikan posisi absolut yang benar dari kolom sistem dan potongan layar, terlepas dari apa yang telah dilakukan tata letak induk. Menggunakan metode Modifier.fitInside atau Modifier.fitOutside akan membantu memperbaiki masalah saat Composables ancestor salah menggunakan inset.
  • Menghindari kolom sistem dengan mudah: Membantu konten aplikasi menghindari kolom sistem dan potongan layar, serta dapat lebih mudah daripada menggunakan WindowInsets secara langsung.
  • Sangat dapat disesuaikan: Developer dapat menyelaraskan konten ke penggaris kustom, dan memiliki kontrol yang tepat atas tata letak dengan tata letak kustom.

Kekurangan WindowInsetsRulers

  • Tidak dapat digunakan untuk Pengukuran: Karena beroperasi selama fase penempatan, informasi posisi yang diberikannya tidak tersedia selama fase pengukuran sebelumnya.

Menyelaraskan konten dengan metode Modifier

Modifier.fitInside memungkinkan aplikasi menyelaraskan konten ke kolom sistem dan potongan layar. Metode ini dapat digunakan sebagai pengganti WindowInsets. Modifier.fitOutside biasanya merupakan kebalikan dari Modifier.fitInside.

Misalnya, untuk memverifikasi bahwa konten aplikasi menghindari kolom sistem dan potongan layar, Anda dapat menggunakan fitInside(WindowInsetsRulers.safeDrawing.current).

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

Tabel berikut menunjukkan tampilan konten aplikasi Anda dengan penggaris yang telah ditentukan dengan Modifier.fitInside atau Modifier.fitOutside.

Jenis penggaris yang telah ditentukan

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

T/A

NavigationBars

SafeDrawing

T/A (gunakan StatusBar, CaptionBar, NavigationBar sebagai gantinya)

StatusBar

Penggunaan Modifier.fitInside dan Modifier.fitOutside mengharuskan composable dibatasi. Artinya, Anda harus menentukan pengubah seperti Modifier.size atau Modifier.fillMaxSize.

Beberapa penggaris seperti Modifier.fitOutside di SafeDrawing dan SystemBars menampilkan beberapa penggaris. Dalam hal ini, Android menempatkan Composable menggunakan satu penggaris dari kiri, atas, kanan, bawah.

Menghindari IME dengan Modifier.fitInside

Untuk menangani elemen bawah dengan IME dengan Modifier.fitInside, teruskan RectRuler yang mengambil nilai terdalam dari NavigationBar dan 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()
        )
    }
}

Menghindari status bar dan caption bar dengan Modifier.fitInside

Demikian pula, untuk memverifikasi elemen atas menghindari status bar dan caption bar bersama dengan Modifier.fitInsider, teruskan RectRuler yang mengambil nilai terdalam dari StatusBars dan CaptionBar.

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

Membuat WindowInsetsRulers kustom

Anda dapat menyelaraskan konten ke penggaris kustom. Misalnya, pertimbangkan kasus penggunaan saat composable induk menangani inset dengan tidak benar sehingga menyebabkan masalah padding pada turunan hilir. Meskipun masalah ini dapat diselesaikan dengan cara lain, termasuk menggunakan Modifier.fitInside, Anda juga dapat membuat penggaris kustom untuk menyelaraskan composable turunan secara tepat tanpa harus memperbaiki masalah di induk upstream seperti yang ditunjukkan dalam contoh dan video berikut:

@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)
            }
        }
    }
}

Video berikut menunjukkan contoh konsumsi inset IME yang bermasalah yang disebabkan oleh induk upstream pada gambar di sebelah kiri, dan menggunakan penggaris kustom untuk memperbaiki masalah di sebelah kanan. Padding tambahan ditampilkan di bawah Composable TextField karena padding menu navigasi tidak digunakan oleh induk. Turunan ditempatkan di lokasi yang benar pada gambar kanan menggunakan penggaris kustom seperti yang terlihat dalam contoh kode sebelumnya.

Memastikan induk dibatasi

Agar dapat menggunakan WindowInsetsRulers dengan aman, pastikan induk memberikan batasan yang valid. Induk harus memiliki ukuran yang ditentukan dan tidak dapat bergantung pada ukuran turunan yang menggunakan WindowInsetsRulers. Gunakan fillMaxSize atau pengubah ukuran lainnya pada Composables induk.

Demikian pula, menempatkan composable yang menggunakan WindowInsetsRulers di dalam penampung scroll seperti verticalScroll dapat menyebabkan perilaku yang tidak terduga karena penampung scroll memberikan batasan tinggi yang tidak terbatas, yang tidak kompatibel dengan logika penggaris.