Tentang WindowInsetsRulers

WindowInsets adalah API standar di Jetpack Compose untuk menangani area layar yang sebagian atau sepenuhnya terhalang oleh UI sistem. Area ini mencakup status bar, menu navigasi, dan keyboard virtual. Atau, Anda dapat 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.

Keunggulan WindowInsetsRulers

  • Menghindari Kompleksitas Konsumsi: Modifier ini beroperasi selama fase penempatan tata letak. Artinya, API ini sepenuhnya mengabaikan rantai pemakaian inset 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 Composable ancestor menggunakan inset secara tidak tepat.
  • Menghindari kolom sistem dengan mudah: Membantu konten aplikasi Anda menghindari kolom sistem dan potongan layar, serta dapat lebih mudah daripada menggunakan WindowInsets secara langsung.
  • Sangat dapat disesuaikan: Developer dapat menyelaraskan konten dengan penggaris kustom, dan memiliki kontrol yang akurat atas tata letak mereka 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 Pengubah

Modifier.fitInside memungkinkan aplikasi menyelaraskan konten dengan 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 sebelumnya 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 panel teks dengan Modifier.fitInside

Demikian pula, untuk memverifikasi elemen teratas menghindari status bar dan panel teks bersama dengan Modifier.fitInside, 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 dengan penggaris kustom. Misalnya, pertimbangkan kasus penggunaan saat composable induk menangani inset secara tidak tepat sehingga menyebabkan masalah padding di turunan downstream. Meskipun masalah ini dapat diselesaikan dengan cara lain, termasuk dengan menggunakan Modifier.fitInside, Anda juga dapat membuat penggaris kustom untuk meratakan 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 TextField Composable karena padding menu navigasi tidak digunakan oleh induk. Anak 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 boleh bergantung pada ukuran turunan yang menggunakan WindowInsetsRulers. Gunakan fillMaxSize atau pengubah ukuran lainnya pada Composable 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.