ข้อมูลเบื้องต้นเกี่ยวกับเลย์เอาต์ของการเขียน

Jetpack Compose ช่วยให้การออกแบบและสร้าง UI ของแอปง่ายขึ้นมาก โดย Compose จะแปลงสถานะเป็นองค์ประกอบ UI ผ่านกระบวนการต่อไปนี้

  1. การเรียบเรียงองค์ประกอบ
  2. เลย์เอาต์ขององค์ประกอบ
  3. การวาดองค์ประกอบ

Compose สถานะการเปลี่ยนผ่านไปยัง UI ผ่านการจัดองค์ประกอบ เลย์เอาต์ การวาด

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

เป้าหมายของเลย์เอาต์ใน Compose

การใช้งานระบบเลย์เอาต์ของ Jetpack Compose มีเป้าหมายหลัก 2 ประการ ได้แก่

ข้อมูลเบื้องต้นเกี่ยวกับฟังก์ชันที่ประกอบกันได้

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

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

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

หากไม่มีคำแนะนำเกี่ยวกับวิธีจัดเรียง องค์ประกอบ Compose จะซ้อนองค์ประกอบข้อความไว้ด้านบนของกันและกัน ทำให้ข้อความอ่านไม่ได้

องค์ประกอบข้อความ 2 รายการที่วาดซ้อนกัน ทำให้ข้อความอ่านไม่ได้

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

คอมโพเนนต์เลย์เอาต์มาตรฐาน

ในหลายๆ กรณี คุณสามารถใช้เพียง องค์ประกอบเลย์เอาต์มาตรฐาน ของ Compose

ใช้ Column เพื่อวางรายการในแนวตั้งบนหน้าจอ

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

องค์ประกอบข้อความ 2 รายการจัดเรียงในเลย์เอาต์คอลัมน์เพื่อให้ข้อความอ่านได้

เช่นเดียวกัน ให้ใช้ Row เพื่อวางรายการในแนวนอนบนหน้าจอ ทั้ง Column และ Row รองรับการกำหนดค่าการจัดแนวขององค์ประกอบที่ประกอบด้วย

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

แสดงเลย์เอาต์ที่ซับซ้อนมากขึ้น โดยมีกราฟิกขนาดเล็กอยู่ข้างคอลัมน์ขององค์ประกอบข้อความ

ใช้ Box เพื่อวางองค์ประกอบไว้ด้านบนขององค์ประกอบอื่น Box ยังรองรับการกำหนดค่าการจัดแนวที่เฉพาะเจาะจงขององค์ประกอบที่ประกอบด้วย

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

แสดงองค์ประกอบ 2 รายการที่ซ้อนกัน

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

เปรียบเทียบ Composable ของเลย์เอาต์ 3 แบบ ได้แก่ คอลัมน์ แถว และกล่อง

หากต้องการกำหนดตำแหน่งขององค์ประกอบย่อยภายใน Row ให้ตั้งค่าอาร์กิวเมนต์ horizontalArrangement และ verticalAlignment ส่วน Column ให้ตั้งค่าอาร์กิวเมนต์ verticalArrangement และ horizontalAlignment

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

จัดแนวรายการไปทางขวา

โมเดลเลย์เอาต์

ในโมเดลเลย์เอาต์ ระบบจะจัดวางแผนผัง UI ในการส่งผ่านครั้งเดียว โดยระบบจะขอให้แต่ละโหนดวัดขนาดของตัวเองก่อน จากนั้นจึงวัดขนาดของโหนดลูกแบบเรียกซ้ำ โดยส่งข้อจำกัดด้านขนาดลงไปตามแผนผังไปยังโหนดลูก จากนั้นระบบจะกำหนดขนาดและวางโหนดใบ โดยส่งขนาดที่กำหนดและวิธีการวางกลับขึ้นไปตามแผนผัง

กล่าวโดยย่อคือ โหนดระดับบนจะวัดขนาดก่อนโหนดระดับล่าง แต่จะกำหนดขนาดและวางหลังจากโหนดระดับล่าง

ลองดูฟังก์ชัน SearchResult ต่อไปนี้

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

ฟังก์ชันนี้จะสร้างแผนผัง UI ต่อไปนี้

SearchResult
  Row
    Image
    Column
      Text
      Text

ในตัวอย่าง SearchResult เลย์เอาต์แผนผัง UI จะเป็นไปตามลำดับต่อไปนี้

  1. ระบบจะขอให้โหนดราก Row วัดขนาด
  2. โหนดราก Row ขอให้โหนดลูกรายการแรก Image วัดขนาด
  3. Image เป็นโหนดใบ (ไม่มีโหนดลูก) จึงรายงานขนาดและส่งวิธีการวางกลับ
  4. โหนดราก Row ขอให้โหนดลูกรายการที่ 2 Column วัดขนาด
  5. โหนด Column ขอให้โหนดลูก Text รายการแรกวัดขนาด
  6. โหนด Text รายการแรกเป็นโหนดใบ จึงรายงานขนาดและส่งวิธีการวางกลับ
  7. โหนด Column ขอให้โหนดลูก Text รายการที่ 2 วัดขนาด
  8. โหนด Text รายการที่ 2 เป็นโหนดใบ จึงรายงานขนาดและส่งวิธีการวางกลับ
  9. เมื่อโหนด Column วัดขนาด กำหนดขนาด และวางโหนดลูกแล้ว ก็จะกำหนดขนาดและตำแหน่งของตัวเองได้
  10. เมื่อโหนดราก Row วัดขนาด กำหนดขนาด และวางโหนดลูกแล้ว ก็จะกำหนดขนาดและตำแหน่งของตัวเองได้

ลำดับการวัด การกำหนดขนาด และการวางตำแหน่งในโครงสร้าง UI ของผลการค้นหาใน Search

ประสิทธิภาพ

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

หากเลย์เอาต์ต้องมีการวัดขนาดหลายครั้งด้วยเหตุผลบางประการ Compose มีระบบพิเศษที่เรียกว่า การวัดขนาดโดยธรรมชาติ อ่านเพิ่มเติมเกี่ยวกับฟีเจอร์นี้ได้ที่ การวัดขนาดโดยธรรมชาติในเลย์เอาต์ Compose

เนื่องจากการวัดขนาดและการวางเป็นระยะย่อยที่แยกกันของการส่งผ่านเลย์เอาต์ การเปลี่ยนแปลงใดๆ ที่ส่งผลต่อการวางรายการเท่านั้น ไม่ใช่การวัดขนาด จึงดำเนินการแยกกันได้

การใช้ตัวปรับแต่งในเลย์เอาต์

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

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

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

ในโค้ดด้านบน ให้สังเกตฟังก์ชันตัวปรับแต่งต่างๆ ที่ใช้ร่วมกัน

  • clickable ทำให้ Composable ตอบสนองต่อข้อมูลจากผู้ใช้และแสดง Ripple
  • padding เพิ่มพื้นที่ว่างรอบๆ องค์ประกอบ
  • fillMaxWidth ทำให้ฟังก์ชันที่ประกอบกันได้เติมความกว้างสูงสุดที่ได้รับจากองค์ประกอบระดับบน
  • size() ระบุความกว้างและความสูงที่ต้องการขององค์ประกอบ

เลย์เอาต์ที่เลื่อนได้

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

สำหรับรายการและรายการแบบ Lazy โปรดดูเอกสารประกอบเกี่ยวกับรายการของ Compose

เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์

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

ข้อจำกัด

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

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

เลย์เอาต์ที่อิงตามสล็อต

Compose มี Composable ที่หลากหลายรายการที่อิงตาม Material Design พร้อมทรัพยากร Dependency androidx.compose.material:material (รวมอยู่ด้วยเมื่อสร้าง โปรเจ็กต์ Compose ใน Android Studio) เพื่อให้การสร้าง UI เป็นเรื่องง่าย โดยมีองค์ประกอบต่างๆ เช่น Drawer, FloatingActionButton, และ TopAppBar

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

แผนภาพแสดงช่องที่ใช้ได้ในแถบแอปของคอมโพเนนต์ Material

โดยปกติแล้วฟังก์ชันที่ประกอบกันได้จะใช้แลมดา content ที่ประกอบกันได้ ( content: @Composable () -> Unit) API สล็อตจะเปิดเผยพารามิเตอร์ content หลายรายการสำหรับการใช้งานที่เฉพาะเจาะจง ตัวอย่างเช่น TopAppBar ช่วยให้คุณระบุเนื้อหาสำหรับ title, navigationIcon และ actions ได้

ตัวอย่างเช่น Scaffold ช่วยให้คุณใช้ UI ที่มีโครงสร้างเลย์เอาต์ Material Design พื้นฐานได้ Scaffoldมีสล็อตสำหรับคอมโพเนนต์ Material ระดับบนสุดที่ใช้กันมากที่สุด เช่น TopAppBar, BottomAppBar, FloatingActionButton, และ Drawer การใช้ Scaffold ช่วยให้มั่นใจได้ว่าคอมโพเนนต์เหล่านี้อยู่ในตำแหน่งที่เหมาะสมและทำงานร่วมกันได้อย่างถูกต้อง

แอปตัวอย่าง JetNews ซึ่งใช้ Scaffold เพื่อจัดตำแหน่งองค์ประกอบหลายรายการ

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}