รองรับขนาดการแสดงผลที่ต่างกัน

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

หากต้องการรองรับขนาดการแสดงผลให้ได้มากที่สุด ไม่ว่าจะเป็นหน้าจออุปกรณ์ต่างๆ หรือหน้าต่างแอปต่างๆ ในโหมดหลายหน้าต่าง ให้ออกแบบเลย์เอาต์ของแอป ให้ปรับเปลี่ยนตามอุปกรณ์และปรับเปลี่ยนตามพื้นที่โฆษณา เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์/ปรับเปลี่ยนตามพื้นที่โฆษณาจะมอบประสบการณ์การใช้งานที่เพิ่มประสิทธิภาพให้กับผู้ใช้ ไม่ว่าขนาดการแสดงผลจะเป็นอย่างไรก็ตาม ซึ่งช่วยให้แอปของคุณรองรับโทรศัพท์ แท็บเล็ต อุปกรณ์แบบพับได้ อุปกรณ์ ChromeOS การวางแนวแนวตั้งและแนวนอน รวมถึงการกำหนดค่าการแสดงผลที่ปรับขนาดได้ เช่น โหมดแยกหน้าจอและการแสดงหน้าต่างเดสก์ท็อป

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

Jetpack Compose เป็นชุดเครื่องมือ UI แบบประกาศสิ่งที่ต้องการ จึงเหมาะอย่างยิ่งสำหรับการออกแบบและใช้เลย์เอาต์ที่เปลี่ยนแปลงแบบไดนามิกเพื่อแสดงเนื้อหาแตกต่างกันในขนาดการแสดงผลต่างๆ

ระบุการเปลี่ยนแปลงเลย์เอาต์ขนาดใหญ่สำหรับ Composables ระดับเนื้อหาอย่างชัดเจน

Composables ระดับแอปและระดับเนื้อหาจะใช้พื้นที่การแสดงผลทั้งหมดที่แอปของคุณใช้ได้ สำหรับ Composables ประเภทนี้ การเปลี่ยนเลย์เอาต์โดยรวมของแอปในการแสดงผลขนาดใหญ่อาจเป็นเรื่องสมเหตุสมผล

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

รูปที่ 1 รูปแบบของโทรศัพท์ อุปกรณ์แบบพับได้ แท็บเล็ต และแล็ปท็อป

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

ให้ตัดสินใจโดยอิงตามส่วนของหน้าจอจริงที่จัดสรรให้กับ แอปของคุณ ซึ่งอธิบายโดยเมตริกหน้าต่างปัจจุบันที่ไลบรารี Jetpack WindowManager ให้ไว้ ดูตัวอย่างวิธีใช้ WindowManager ในแอป Compose ได้ที่ตัวอย่าง JetNews

การทำให้เลย์เอาต์ปรับให้เข้ากับพื้นที่การแสดงผลที่พร้อมใช้งานยังช่วยลดปริมาณการจัดการพิเศษที่จำเป็นในการรองรับแพลตฟอร์มอย่าง ChromeOS และรูปแบบของอุปกรณ์อย่างแท็บเล็ตและอุปกรณ์แบบพับได้ด้วย

เมื่อกำหนดเมตริกของพื้นที่ที่แอปของคุณใช้ได้แล้ว ให้แปลง ขนาดดิบเป็นคลาสขนาดหน้าต่างตามที่อธิบายไว้ในหัวข้อใช้คลาสขนาดหน้าต่าง คลาสขนาดหน้าต่างเป็นเบรกพอยต์ที่ออกแบบมาเพื่อสร้างสมดุลระหว่างความเรียบง่ายของตรรกะของแอปกับความยืดหยุ่นในการเพิ่มประสิทธิภาพแอปสำหรับขนาดการแสดงผลส่วนใหญ่

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

@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass
) {
    // Decide whether to show the top app bar based on window size class.
    val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND)

    // MyScreen logic is based on the showTopAppBar boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

แนวทางแบบเลเยอร์จะจำกัดตรรกะขนาดการแสดงผลไว้ในตำแหน่งเดียวแทนที่จะกระจายไปทั่วแอปในหลายๆ ที่ที่ต้องซิงค์กัน ตำแหน่งเดียวจะสร้างสถานะ ซึ่งสามารถส่งลงไปใน Composables อื่นๆ ได้อย่างชัดเจน เช่นเดียวกับสถานะแอปอื่นๆ การส่งสถานะลงไปอย่างชัดเจนจะทำให้ Composables แต่ละรายการง่ายขึ้น เนื่องจาก Composables จะใช้คลาสขนาดหน้าต่างหรือการกำหนดค่าที่ระบุพร้อมกับข้อมูลอื่นๆ

Composables ที่ซ้อนกันแบบยืดหยุ่นสามารถใช้ซ้ำได้

Composables จะใช้ซ้ำได้มากขึ้นเมื่อวางไว้ในตำแหน่งที่หลากหลายได้ หากต้องวาง Composable ในตำแหน่งที่เฉพาะเจาะจงด้วยขนาดที่เฉพาะเจาะจง ก็เป็นไปได้ยากที่ Composable จะใช้ซ้ำได้ในบริบทอื่นๆ ซึ่งหมายความว่า Composables แต่ละรายการที่ใช้ซ้ำได้ควรหลีกเลี่ยงการขึ้นอยู่กับข้อมูลขนาดการแสดงผล ส่วนกลาง โดยนัย

ลองนึกภาพ Composable ที่ซ้อนกันซึ่งใช้เลย์เอาต์รายละเอียดรายการ ซึ่งอาจแสดงบานหน้าต่างเดียวหรือ 2 บานหน้าต่างข้างกัน

แอปที่แสดง 2 บานหน้าต่างเคียงข้างกัน
รูปที่ 2 แอปที่แสดงเลย์เอาต์รายละเอียดรายการทั่วไป โดย 1 คือพื้นที่รายการ ส่วน 2 คือพื้นที่รายละเอียด

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

@Composable
fun AdaptivePane(
    showOnePane: Boolean,
    /* ... */
) {
    if (showOnePane) {
        OnePane(/* ... */)
    } else {
        TwoPane(/* ... */)
    }
}

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

รูปที่ 3 การ์ดแคบที่แสดงเพียงไอคอนและชื่อ และการ์ดกว้างที่แสดงไอคอน ชื่อ และคำอธิบายสั้นๆ

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

เนื่องจาก Composable ไม่ใช่ Composable ระดับเนื้อหา จึงไม่ควรใช้เมตริกหน้าต่างปัจจุบันโดยตรง

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

ใช้ความกว้างที่กำหนดให้กับ Composable จริงๆ เพื่อแสดงผล คุณมี 2 ตัวเลือกในการรับความกว้างดังกล่าว

  • หากต้องการเปลี่ยน ตำแหน่ง หรือ วิธี แสดงเนื้อหา ให้ใช้ คอลเล็กชันตัวปรับแต่งหรือเลย์เอาต์ที่กำหนดเอง เพื่อทำให้เลย์เอาต์ ปรับเปลี่ยนตามอุปกรณ์ วิธีนี้อาจง่ายเพียงแค่ให้องค์ประกอบย่อยเติมพื้นที่ที่พร้อมใช้งานทั้งหมด หรือจัดวางองค์ประกอบย่อยด้วยหลายคอลัมน์หากมีพื้นที่เพียงพอ

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

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }
}

ทำให้ข้อมูลทั้งหมดพร้อมใช้งานสำหรับขนาดการแสดงผลต่างๆ

เมื่อใช้ Composable ที่ใช้ประโยชน์จากพื้นที่การแสดงผลเพิ่มเติม คุณอาจอยากใช้ข้อมูลอย่างมีประสิทธิภาพและโหลดข้อมูลเป็นผลข้างเคียงของขนาดการแสดงผลปัจจุบัน

อย่างไรก็ตาม การทำเช่นนี้ขัดต่อหลักการของโฟลว์ข้อมูลแบบทิศทางเดียว ซึ่งข้อมูลสามารถยกขึ้นและส่งไปยัง Composables เพื่อแสดงผลได้อย่างเหมาะสม ควรส่งข้อมูลไปยัง Composable ให้เพียงพอเพื่อให้ Composable มีเนื้อหาเพียงพอสำหรับขนาดการแสดงผลทุกขนาดเสมอ แม้ว่าเนื้อหาบางส่วนอาจไม่ได้ใช้เสมอไปก็ตาม

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(description)
                }
                Image(imageUrl)
            }
        }
    }
}

จากตัวอย่าง Card โปรดทราบว่าระบบจะส่ง description ไปยัง Card เสมอ แม้ว่าจะใช้ description ก็ต่อเมื่อความกว้างอนุญาตให้แสดงได้ แต่ Card จะต้องมี description เสมอ ไม่ว่าความกว้างที่พร้อมใช้งานจะเป็นเท่าใดก็ตาม

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

หลักการนี้ยังช่วยให้รักษาสถานะไว้ได้เมื่อมีการเปลี่ยนแปลงเลย์เอาต์ การยกข้อมูลที่อาจไม่ได้ใช้ในขนาดการแสดงผลทั้งหมดขึ้นมาจะช่วยให้คุณรักษาสถานะแอปไว้ได้เมื่อขนาดเลย์เอาต์เปลี่ยนแปลง

ตัวอย่างเช่น คุณสามารถยกแฟล็กบูลีน showMore ขึ้นมาเพื่อให้ระบบรักษาสถานะแอปไว้เมื่อการปรับขนาดการแสดงผลทำให้เลย์เอาต์สลับระหว่างการซ่อนและการแสดงเนื้อหา

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    var showMore by remember { mutableStateOf(false) }

    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(
                        description = description,
                        showMore = showMore,
                        onShowMoreToggled = { newValue ->
                            showMore = newValue
                        }
                    )
                }
                Image(imageUrl)
            }
        }
    }
}

ดูข้อมูลเพิ่มเติม

ดูแหล่งข้อมูลต่อไปนี้เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ใน Compose

แอปตัวอย่าง

  • CanonicalLayouts เป็นที่เก็บรูปแบบการออกแบบที่ได้รับการพิสูจน์แล้ว ว่ามอบประสบการณ์การใช้งานที่ดีที่สุดให้กับผู้ใช้ในการแสดงผลขนาดใหญ่
  • JetNews แสดงวิธีออกแบบแอปที่ปรับ UI เพื่อ ใช้พื้นที่การแสดงผลที่พร้อมใช้งาน
  • Reply เป็นตัวอย่างที่ปรับเปลี่ยนตามอุปกรณ์สำหรับการรองรับโทรศัพท์มือถือ แท็บเล็ต และอุปกรณ์แบบพับได้
  • Now in Android เป็นแอปที่ใช้เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์เพื่อ รองรับขนาดการแสดงผลต่างๆ

วิดีโอ