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

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

  1. กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) ขึ้นไปเพื่อบังคับใช้การแสดงผลแบบขอบต่อขอบ ใน Android 15 ขึ้นไป แอปของคุณแสดงอยู่ด้านหลัง UI ของระบบ คุณ ปรับ UI ของแอปได้โดยการจัดการ Inset
  2. หรือจะเรียกใช้ enableEdgeToEdge() ใน Activity.onCreate() ก็ได้ ซึ่งจะช่วยให้แอปของคุณแสดงแบบไร้ขอบใน Android เวอร์ชันก่อนหน้า
  3. ตั้งค่า android:windowSoftInputMode="adjustResize" ในรายการAndroidManifest.xmlกิจกรรม การตั้งค่านี้ช่วยให้แอปได้รับขนาด ของ IME ซอฟต์แวร์เป็นระยะขอบ ซึ่งจะช่วยให้คุณใช้เลย์เอาต์ที่เหมาะสม และการเว้นวรรคเมื่อ IME ปรากฏและหายไปในแอป

    <!-- In your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

ใช้ Compose API

เมื่อกิจกรรมของคุณควบคุมการจัดการระยะขอบทั้งหมดแล้ว คุณจะใช้ Compose API เพื่อให้มั่นใจว่าเนื้อหาจะไม่ถูกบดบังและองค์ประกอบที่โต้ตอบได้จะไม่ ทับซ้อนกับ 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 นอกจากนี้ เลย์เอาต์ทั้งหมดที่ใช้ระยะขอบเหล่านี้จะมีการเคลื่อนไหวโดยอัตโนมัติเมื่อค่าระยะขอบเปลี่ยนแปลง

การใช้ประเภท Inset เหล่านี้เพื่อปรับเลย์เอาต์ที่ประกอบได้มี 2 วิธีหลักๆ ได้แก่ ตัวแก้ไข Padding และตัวแก้ไขขนาด Inset

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

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

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

ระยะขอบและระยะของ Jetpack Compose

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

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