ตั้งค่าส่วนที่เว้นไว้ในหน้าต่าง

เมื่อ Activity ควบคุมการจัดการ Inset ทั้งหมดแล้ว คุณจะใช้ API ของ Compose เพื่อยืนยันว่าเนื้อหาไม่ถูกบดบังและองค์ประกอบที่โต้ตอบได้ไม่ ทับซ้อนกับ UI ของระบบ นอกจากนี้ API เหล่านี้ยังซิงค์เลย์เอาต์ของแอปกับการเปลี่ยนแปลงส่วนที่เว้นไว้ด้วย

จัดการส่วนที่เว้นไว้โดยใช้ตัวปรับแต่งขนาดหรือระยะห่างจากขอบ

ตัวอย่างเช่น นี่คือวิธีพื้นฐานที่สุดในการใช้ระยะขอบกับเนื้อหา ของทั้งแอป

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

ข้อมูลโค้ดนี้ใช้ส่วนที่เว้นไว้ของหน้าต่าง safeDrawing เป็นระยะห่างจากขอบรอบๆ เนื้อหาทั้งหมดของแอป แม้ว่าวิธีนี้จะช่วยให้มั่นใจได้ว่าองค์ประกอบที่โต้ตอบได้จะไม่ ทับซ้อนกับ UI ของระบบ แต่ก็หมายความว่าไม่มีส่วนใดของแอปที่จะวาดอยู่ด้านหลัง UI ของระบบเพื่อให้ได้เอฟเฟกต์แบบไร้ขอบ หากต้องการใช้ประโยชน์จากทั้งหน้าต่างอย่างเต็มที่ คุณต้องปรับแต่งตำแหน่งที่ใช้ระยะขอบในบนหน้าจอแต่ละหน้าจอ หรือคอมโพเนนต์แต่ละคอมโพเนนต์

ประเภทส่วนที่เว้นไว้ทั้งหมดนี้จะเคลื่อนไหวโดยอัตโนมัติด้วยภาพเคลื่อนไหวของ IME ที่พอร์ตกลับไปยัง API 21 นอกจากนี้ เลย์เอาต์ทั้งหมดที่ใช้ระยะขอบเหล่านี้จะเคลื่อนไหวโดยอัตโนมัติด้วยเมื่อค่าระยะขอบเปลี่ยนแปลง

การจัดการส่วนที่เว้นไว้เพื่อปรับเลย์เอาต์ที่ใช้ Composable ทำได้ 3 วิธีดังนี้

ตัวแก้ไขระยะห่างจากขอบ

Modifier.windowInsetsPadding(windowInsets: WindowInsets) จะใช้ ระยะขอบหน้าต่างที่ระบุเป็นระยะเว้นวรรค ซึ่งจะทำงานเหมือนกับ Modifier.padding เช่น Modifier.windowInsetsPadding(WindowInsets.safeDrawing) จะใช้ ระยะขอบที่ปลอดภัยในการวาดเป็นระยะห่างจากขอบทั้ง 4 ด้าน

นอกจากนี้ ยังมีเมธอดยูทิลิตีในตัวหลายรายการสำหรับประเภทการแทรกที่พบบ่อยที่สุด Modifier.safeDrawingPadding() เป็นหนึ่งในวิธีดังกล่าว ซึ่งเทียบเท่ากับ Modifier.windowInsetsPadding(WindowInsets.safeDrawing) และมีตัวแก้ไขที่คล้ายกัน สำหรับขอบแทรกประเภทอื่นๆ

ตัวปรับแต่งขนาดส่วนที่เว้นไว้

ตัวแก้ไขต่อไปนี้จะใช้จำนวนการแทรกหน้าต่างโดยการตั้งค่าขนาดของ คอมโพเนนต์ให้เป็นขนาดของการแทรก

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

ใช้ด้านเริ่มต้นของ WindowInsets เป็นความกว้าง (เช่น Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

ใช้ด้านสิ้นสุดของ WindowInsets เป็นความกว้าง (เช่น Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

ใช้ด้านบนของ WindowInsets เป็นความสูง (เช่น Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

ใช้ด้านล่างของ WindowInsets เป็นความสูง (เช่น Modifier.height)

ตัวแก้ไขเหล่านี้มีประโยชน์อย่างยิ่งสำหรับการกำหนดขนาด Spacer ที่ใช้พื้นที่ของระยะขอบ

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

การรับชมแบบภาพซ้อนภาพ

ตัวปรับแต่งระยะขอบด้านใน (windowInsetsPadding และตัวช่วยอย่าง safeDrawingPadding) จะใช้ส่วนของระยะขอบด้านในที่ ใช้เป็นระยะห่างจากขอบโดยอัตโนมัติ ในขณะที่เจาะลึกเข้าไปในแผนผังการจัดองค์ประกอบ ตัวปรับแต่งระยะห่างจากขอบภายในที่ซ้อนกัน และตัวปรับแต่งขนาดส่วนที่เว้นไว้ภายในจะทราบว่าตัวปรับแต่งระยะห่างจากขอบภายในด้านนอกได้ใช้ส่วนที่เว้นไว้ภายในบางส่วนไปแล้ว และหลีกเลี่ยง การใช้ส่วนที่เว้นไว้ภายในส่วนเดียวกันมากกว่า 1 ครั้ง ซึ่งจะทำให้มีพื้นที่ว่างมากเกินไป

ตัวปรับแต่งขนาดส่วนที่เว้นไว้ยังหลีกเลี่ยงการใช้ส่วนที่เว้นไว้ส่วนเดียวกันมากกว่า 1 ครั้ง หากมีการใช้ส่วนที่เว้นไว้ไปแล้ว อย่างไรก็ตาม เนื่องจากมีการเปลี่ยนขนาดโดยตรง จึงไม่ได้ใช้ระยะขอบเอง

ด้วยเหตุนี้ ตัวแก้ไขระยะขอบที่ซ้อนกันจึงเปลี่ยนจำนวน ระยะขอบที่ใช้กับแต่ละ Composable โดยอัตโนมัติ

ดูLazyColumnตัวอย่างเดียวกันกับก่อนหน้านี้ LazyColumnจะได้รับการปรับขนาดโดยตัวแก้ไข imePadding ภายใน LazyColumn รายการสุดท้ายจะมีขนาดเท่ากับความสูงของด้านล่างของแถบระบบ ดังนี้

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

เมื่อปิด IME ตัวแก้ไข imePadding() จะไม่มีการเพิ่มระยะขอบ เนื่องจาก IME ไม่มีส่วนสูง เนื่องจากตัวแก้ไข imePadding() ไม่ได้ใช้ระยะห่างจากขอบ จึงไม่มีการใช้ระยะขอบด้านใน และความสูงของ Spacer จะเป็นขนาดของ ด้านล่างของแถบระบบ

เมื่อ IME เปิดขึ้น ขอบแทรกของ IME จะเคลื่อนไหวให้มีขนาดเท่ากับ IME และimePadding()ตัวแก้ไขจะเริ่มใช้การเว้นวรรคด้านล่างเพื่อปรับขนาดLazyColumnเมื่อ IME เปิดขึ้น เมื่อตัวแก้ไข imePadding() เริ่มใช้ ระยะห่างจากด้านล่าง ก็จะเริ่มใช้ระยะห่างจากขอบในจำนวนดังกล่าวด้วย ดังนั้น ความสูงของ Spacer จะเริ่มลดลง เนื่องจากตัวแก้ไข imePadding() ได้ใช้การเว้นวรรคสำหรับแถบระบบ แล้ว เมื่อ imePadding() ตัวแก้ไขใช้ระยะห่างจากขอบด้านล่างที่มีขนาดใหญ่กว่า แถบระบบ ความสูงของ Spacer จะเป็น 0

เมื่อ IME ปิดลง การเปลี่ยนแปลงจะเกิดขึ้นในทางกลับกัน โดย Spacer จะเริ่ม ขยายจากความสูง 0 เมื่อ imePadding() ใช้ความสูงน้อยกว่า ด้านล่างของแถบระบบ จนกระทั่งในที่สุด Spacer จะมีความสูงเท่ากับ ด้านล่างของแถบระบบเมื่อ IME เคลื่อนไหวออกไปจนหมด

รูปที่ 2 คอลัมน์เลซี่แบบขอบจรดขอบที่มี TextField

ลักษณะการทำงานนี้เกิดขึ้นผ่านการสื่อสารระหว่างตัวแก้ไขทั้งหมด windowInsetsPadding และสามารถปรับเปลี่ยนได้ด้วยวิธีอื่นๆ อีก 2-3 วิธี

Modifier.consumeWindowInsets(insets: WindowInsets) ยังใช้ระยะขอบภายใน ในลักษณะเดียวกับ Modifier.windowInsetsPadding แต่ไม่ได้ใช้ ระยะขอบภายในที่ใช้เป็นระยะเว้น ซึ่งจะเป็นประโยชน์เมื่อใช้ร่วมกับตัวแก้ไข inset size เพื่อระบุให้องค์ประกอบที่อยู่ติดกันทราบว่ามีการใช้ระยะขอบภายในไปแล้ว จำนวนหนึ่ง

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) ทำงานคล้ายกับเวอร์ชันที่มีอาร์กิวเมนต์ WindowInsets มาก แต่ใช้ PaddingValues ที่กำหนดเอง ซึ่งมีประโยชน์ในการแจ้งให้บุตรหลานทราบเมื่อมีการเว้นขอบหรือการเว้นระยะห่างโดยกลไกอื่นที่ไม่ใช่ตัวปรับแต่งการเว้นขอบด้านใน เช่น Modifier.padding หรือตัวเว้นวรรคที่มีความสูงคงที่

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

ในกรณีที่ต้องการใช้ Window Insets ดิบโดยไม่ต้องใช้ ให้ใช้ค่า WindowInsets โดยตรง หรือใช้ WindowInsets.asPaddingValues() เพื่อ แสดงผล PaddingValues ของ Insets ที่ไม่ได้รับผลกระทบจากการใช้ อย่างไรก็ตาม เนื่องจากข้อควรระวังต่อไปนี้ เราขอแนะนำให้ใช้ตัวแก้ไขระยะห่างจากขอบหน้าต่าง และตัวแก้ไขขนาดขอบหน้าต่างทุกครั้งที่เป็นไปได้

ระยะขอบและเฟสของ Jetpack Compose

Compose ใช้ AndroidX Core API พื้นฐานเพื่ออัปเดตและเคลื่อนไหว Inset ซึ่งใช้ Platform API พื้นฐานที่จัดการ Inset เนื่องจากลักษณะการทำงานของแพลตฟอร์มดังกล่าว ขอบจึงมีความสัมพันธ์พิเศษกับเฟสของ Jetpack Compose

ค่าของ Insets จะได้รับการอัปเดตหลังจากเฟสการคอมโพสิต แต่ก่อนเฟสเลย์เอาต์ ซึ่งหมายความว่าการอ่านค่าของ Insets ใน Composition โดยทั่วไปจะใช้ค่าของ Insets ที่ช้าไป 1 เฟรม ตัวปรับแต่งในตัวที่อธิบายไว้ในหน้านี้สร้างขึ้นเพื่อหน่วงเวลาการใช้ค่าของส่วนที่เว้นไว้จนกว่าจะถึงเฟสเลย์เอาต์ ซึ่งจะช่วยให้มั่นใจได้ว่าระบบจะใช้ค่าส่วนที่เว้นไว้ในเฟรมเดียวกันกับที่อัปเดต