เกี่ยวกับ WindowInsetsRulers

WindowInsets เป็น API มาตรฐานใน Jetpack Compose สำหรับการจัดการ พื้นที่ของหน้าจอที่ UI ของระบบบดบังบางส่วนหรือทั้งหมด โดย พื้นที่เหล่านี้รวมถึงแถบสถานะ แถบนำทาง และแป้นพิมพ์บนหน้าจอ หรือจะส่ง WindowInsetsRulers ที่กำหนดไว้ล่วงหน้า เช่น SafeDrawing ไปยัง Modifier.fitInside หรือ Modifier.fitOutside เพื่อจัดแนวเนื้อหาให้ตรงกับแถบระบบและหน้าจอรอยบาก หรือสร้าง WindowInsetsRulers ที่กำหนดเองก็ได้

ข้อดีของ WindowInsetsRulers

  • หลีกเลี่ยงความซับซ้อนในการใช้งาน: ทำงานในระยะการจัดวาง ของเลย์เอาต์ ซึ่งหมายความว่าการดำเนินการนี้จะข้ามห่วงโซ่การใช้งานส่วนที่เว้นไว้โดยสมบูรณ์ และระบุตำแหน่งที่ถูกต้องและแน่นอนของแถบระบบและหน้าจอรอยบากได้เสมอ ไม่ว่าเลย์เอาต์หลักจะทำอะไรก็ตาม การใช้เมธอด Modifier.fitInside หรือ Modifier.fitOutside จะช่วยแก้ไขปัญหา เมื่อ Composable ระดับบนใช้ Insets อย่างไม่ถูกต้อง
  • หลีกเลี่ยงแถบระบบได้อย่างง่ายดาย: ช่วยให้เนื้อหาแอปหลีกเลี่ยงแถบระบบและหน้าจอรอยบาก และอาจตรงไปตรงมากว่าการใช้ 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 ต้องมีการจำกัด Composable ซึ่งหมายความว่าคุณต้องกำหนดตัวแก้ไข เช่น Modifier.size หรือ Modifier.fillMaxSize

ไม้บรรทัดบางอัน เช่น Modifier.fitOutside ใน SafeDrawing และ SystemBars จะแสดง ไม้บรรทัดหลายอัน ในกรณีนี้ Android จะวาง Composable โดยใช้ไม้บรรทัด 1 อัน จากซ้าย บน ขวา ล่าง

หลีกเลี่ยง 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 ที่กำหนดเอง

คุณจัดแนวเนื้อหากับไม้บรรทัดที่กำหนดเองได้ ตัวอย่างเช่น พิจารณากรณีการใช้งานที่ Composable หลักจัดการ Inset อย่างไม่ถูกต้อง ซึ่งทำให้เกิดปัญหาการเว้นวรรคใน Composable ย่อยที่อยู่ปลายน้ำ แม้ว่าปัญหานี้จะแก้ไขได้ด้วยวิธีอื่นๆ รวมถึงการใช้ Modifier.fitInside แต่คุณก็สร้างไม้บรรทัดที่กำหนดเองเพื่อจัดแนว 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 เนื่องจากองค์ประกอบระดับบนสุดไม่ได้ใช้ระยะห่างของแถบนำทาง โดยวาง child ในตำแหน่งที่ถูกต้องในรูปภาพด้านขวาโดยใช้ไม้บรรทัดที่กำหนดเอง ดังที่เห็นในตัวอย่างโค้ดก่อนหน้า

ยืนยันว่าผู้ปกครองถูกจำกัด

หากต้องการใช้ WindowInsetsRulers อย่างปลอดภัย โปรดตรวจสอบว่าผู้ปกครองได้ระบุข้อจำกัดที่ถูกต้อง องค์ประกอบหลักต้องมีขนาดที่กำหนดไว้และไม่สามารถขึ้นอยู่กับขนาดขององค์ประกอบย่อยที่ใช้ WindowInsetsRulers ใช้ fillMaxSize หรือตัวปรับแต่งขนาดอื่นๆ ใน Composable ระดับบนสุด

ในทำนองเดียวกัน การวาง Composable ที่ใช้ WindowInsetsRulers ภายใน คอนเทนเนอร์เลื่อน เช่น verticalScroll อาจทำให้เกิดลักษณะการทำงานที่ไม่คาดคิด เนื่องจาก คอนเทนเนอร์เลื่อนมีข้อจำกัดด้านความสูงที่ไม่จำกัด ซึ่ง ไม่สอดคล้องกับตรรกะของไม้บรรทัด