รองรับหน้าจอขนาดต่างๆ

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

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

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

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

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

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

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

แผนภาพที่แสดงรูปแบบของอุปกรณ์ต่างๆ ซึ่งรวมถึงโทรศัพท์ อุปกรณ์แบบพับได้ แท็บเล็ต และแล็ปท็อป
รูปที่ 1 รูปแบบของอุปกรณ์ โทรศัพท์ อุปกรณ์แบบพับได้ แท็บเล็ต และแล็ปท็อป

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

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

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

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

@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
) {
    // Perform logic on the size class to decide whether to show the top app bar.
    val showTopAppBar = windowSizeClass.windowHeightSizeClass != WindowHeightSizeClass.COMPACT

    // MyScreen knows nothing about window sizes, and performs logic based on a Boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

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

คอมโพสิเบิลที่ฝังไว้อย่างยืดหยุ่นสามารถนำมาใช้ซ้ำได้

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

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

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

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

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

จะเกิดอะไรขึ้นหากเราต้องการให้ Composable เปลี่ยนเลย์เอาต์อย่างอิสระตามพื้นที่ว่างที่มีอยู่ เช่น การ์ดที่ต้องการแสดงรายละเอียดเพิ่มเติมหากมีพื้นที่เพียงพอ เราต้องการใช้ตรรกะตามขนาดที่มี แต่จะเน้นขนาดใดโดยเฉพาะ

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

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

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

ดังนั้น เราควรใช้ความกว้างที่คอมโพสิเบิลได้รับจริงเพื่อแสดงผล เรามี 2 วิธีในการรับค่าความกว้างนั้น

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

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

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

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

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

อย่างไรก็ตาม การทำเช่นนี้ขัดกับหลักการของโฟลว์ข้อมูลแบบทิศทางเดียว ซึ่งก็คือข้อมูลสามารถสร้างขึ้นและจัดเตรียมให้กับ 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 Boolean flag ขึ้นเพื่อให้ระบบรักษาสถานะของผู้ใช้ไว้เมื่อการปรับขนาดทําให้เลย์เอาต์สลับระหว่างการซ่อนและแสดงคําอธิบาย ดังนี้

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

วิดีโอ