เกี่ยวกับ 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 อาจทำให้เกิดลักษณะการทำงานที่ไม่คาดคิด เนื่องจาก คอนเทนเนอร์เลื่อนมีข้อจำกัดด้านความสูงที่ไม่จำกัด ซึ่ง ไม่สอดคล้องกับตรรกะของไม้บรรทัด