เลย์เอาต์ท่าทางบนโต๊ะด้วย Grid และ mediaQuery

ไอคอนคะแนน 4 ดาว

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

อุปกรณ์แบบพับได้ในท่าตั้งโต๊ะแสดงเนื้อหาวิดีโอบนหน้าจอด้านบนและตัวควบคุมการเล่นบนหน้าจอด้านล่าง
รูปที่ 1 ท่าทางแบบวางบนโต๊ะจะแบ่งเนื้อหาและตัวควบคุมแบบโต้ตอบออกเป็น 2 ส่วนโดยมีรอยพับเป็นตัวแบ่ง

แนวทางปฏิบัติแนะนำ

หากต้องการเพิ่มการรองรับท่าทางแบบวางบนโต๊ะลงในแอป ให้ใช้ Grid API เพื่อกำหนดเลย์เอาต์ที่มี 2 แถวและ 1 คอลัมน์ แล้วใช้สื่อการค้นหาท่าทางของหน้าต่างแบบวางบนโต๊ะ

  • ตรวจหาท่าทางแบบวางบนโต๊ะแบบโต้ตอบ: ใช้คอมโพสได้ mediaQuery เพื่อสังเกตสถานะของอุปกรณ์ เมื่อพับอุปกรณ์ให้อยู่ในท่าทางแบบวางบนโต๊ะ (windowPosture == Posture.Tabletop) ให้เปลี่ยนเลย์เอาต์แบบไดนามิกโดยไม่ต้องรีสตาร์ทกิจกรรมหรือจัดการวงจรการทำงานที่ซับซ้อน
  • จัดโครงสร้างเลย์เอาต์ด้วย Grid: เมื่ออยู่ในท่าทางแบบวางบนโต๊ะ ให้ใช้ Grid API (2 rows, 1 column) เพื่อแยกเนื้อหาภาพหลักออกเป็นแถวบนครึ่งหน้าบน และตัวควบคุมแบบโต้ตอบออกเป็นแถวล่างครึ่งหน้าล่างอย่างชัดเจน
  • ใช้ประโยชน์จากการจัดทำดัชนี z โดยนัยสำหรับภาพซ้อนทับ: ในท่าทางมาตรฐาน (ที่ไม่ใช่แบบวางบนโต๊ะ) ให้กำหนดรหัสพื้นที่หลายรายการให้กับเซลล์เดียวกันเพื่อซ้อนทับตัวควบคุมบนเพลเยอร์ Grid จะแสดงผลรายการจากด้านหลังไปด้านหน้าตามลำดับการประกาศโดยอัตโนมัติ
  • ให้การเปลี่ยนภาพเป็นไปอย่างราบรื่น: ตรวจสอบว่ามีการเก็บรักษาสถานะและตำแหน่งการเลื่อนไว้เมื่อเปลี่ยนระหว่างเลย์เอาต์แบบเต็มหน้าจอมาตรฐานกับท่าทางแบบวางบนโต๊ะ

ส่วนผสม

  • Grid: คอนเทนเนอร์เลย์เอาต์แบบปรับเปลี่ยนตามอุปกรณ์ที่ช่วยให้คุณวางรายการในแถวและคอลัมน์ที่กำหนดค่าได้ ซึ่งเหมาะสำหรับการแบ่งเนื้อหาออกเป็นภูมิภาคหน้าจอที่แตกต่างกัน
  • mediaQuery: Jetpack Compose API ที่ใช้เพื่อค้นหาท่าทางของหน้าต่างปัจจุบัน ขนาดคลาส และลักษณะฮาร์ดแวร์ของแอปโดยตรงภายในคอมโพสได้

ขั้นตอน

ค้นหาท่าทางของอุปกรณ์โดยใช้ mediaQuery() เมื่ออุปกรณ์อยู่ในท่าทางแบบวางบนโต๊ะ ให้จัดเรียงเนื้อหาของแอปเป็นตารางกริดแนวตั้งที่มี 2 แถวและ 1 คอลัมน์ โดยตรวจสอบว่าเนื้อหาหลักอยู่ด้านบนและตัวควบคุมยังคงเข้าถึงได้ที่ด้านล่าง

1. กำหนดการกำหนดค่าตารางกริดสำหรับโหมดวางบนโต๊ะ

ภายในฟังก์ชันที่ประกอบกันได้ ให้กำหนดรหัสพื้นที่เลย์เอาต์โดยใช้ Enum และตั้งค่าบล็อก GridConfigurationScope สำหรับท่าทางเริ่มต้นและท่าทางแบบวางบนโต๊ะ

ใน defaultConfig พื้นที่ทั้ง 2 จะกำหนดให้กับเซลล์เดียวกัน (row = 1, column = 1) Grid ใช้การจัดทำดัชนี z โดยนัย ซึ่งคอมโพสได้ที่ประกาศไว้ในเลย์เอาต์จะแสดงผลอยู่เหนือคอมโพสได้ก่อนหน้า ทำให้ตัวควบคุมซ้อนทับเพลเยอร์ได้โดยไม่ต้องซ้อนคอนเทนเนอร์เพิ่มเติม (ดูรายละเอียดเพิ่มเติมได้ที่ ภาพซ้อนทับองค์ประกอบ UI ด้วยตารางกริด)

enum class VideoPlayerArea { Player, Controller }

object PlayerGridConfig {
    // Default: Single area layout with overlay (controls on top of player)
    val defaultConfig: GridConfigurationScope.() -> Unit = {
        row(1f)
        column(1f)
        area(areaId = VideoPlayerArea.Player, row = 1, column = 1)
        area(areaId = VideoPlayerArea.Controller, row = 1, column = 1)
    }

    // Tabletop: Two rows splitting content across the fold
    val tabletopConfig: GridConfigurationScope.() -> Unit = {
        row(0.5f)
        row(0.5f)
        column(1f)
        area(areaId = VideoPlayerArea.Player, row = 1, column = 1) // Top half above fold
        area(areaId = VideoPlayerArea.Controller, row = 2, column = 1) // Bottom half below fold
    }
}

2. ตรวจหาท่าทางแบบวางบนโต๊ะและใช้การกำหนดค่ากับตารางกริด

ใช้ mediaQuery เพื่อเลือกการกำหนดค่าที่ใช้งานอยู่ตาม windowPosture ส่ง config ไปยัง Grid และผูกเนื้อหาคอมโพสได้กับรหัสพื้นที่ การประกาศ VideoPlayerArea.Player ก่อน VideoPlayerArea.Controller จะช่วยให้มั่นใจได้ว่าเมื่อทั้ง 2 รายการแชร์เซลล์ใน defaultConfig ตัวควบคุมจะแสดงผลอยู่เหนือเพลเยอร์

val config = mediaQuery {
    when (windowPosture) {
        Posture.Tabletop -> PlayerGridConfig.tabletopConfig
        else -> PlayerGridConfig.defaultConfig
    }
}

Grid(config = config) {
    Box(
        modifier = Modifier
            .gridItem(areaId = VideoPlayerArea.Player)
    ) {
        // VideoPlayerContent
    }

    Box(
        modifier = Modifier
            .gridItem(areaId = VideoPlayerArea.Controller)
    ) {
        // PlaybackControlsContent
    }
}

ผลลัพธ์

การรวม mediaQuery และ Grid เข้าด้วยกันจะช่วยให้แอปตรวจหาแบบไดนามิกได้เมื่ออุปกรณ์พับได้พับบางส่วนให้อยู่ในท่าทางแบบวางบนโต๊ะ และปรับ UI เป็นพื้นที่แสดงผลที่ใช้งานได้ 2 ส่วนโดยมีรอยพับเป็นตัวแบ่งโดยอัตโนมัติ

แหล่งข้อมูลเพิ่มเติม