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

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

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

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

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

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

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

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

ฟังก์ชันที่ประกอบกันได้เป็นองค์ประกอบพื้นฐานของ Compose ฟังก์ชันที่ประกอบกันได้คือฟังก์ชันที่แสดงผล Unit ซึ่งอธิบายบางส่วนของ UI ฟังก์ชันจะรับอินพุตบางอย่างและสร้างสิ่งที่แสดงบนหน้าจอ ดูข้อมูลเพิ่มเติมเกี่ยวกับ Composable ได้ที่เอกสารประกอบเกี่ยวกับโมเดลทางความคิดของ 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 รายการซ้อนกัน

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

เปรียบเทียบคอมโพสิเบิลเลย์เอาต์แบบง่าย 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 จะจัดวางในรอบเดียว ระบบจะขอให้แต่ละโหนดวัดขนาดตัวเองก่อน จากนั้นจึงวัดขนาดโหนดย่อยซ้ำๆ โดยส่งข้อจำกัดด้านขนาดลงในโครงสร้างไปยังโหนดย่อย จากนั้นระบบจะปรับขนาดและจัดวางโหนด Leaf โดยส่งขนาดที่แก้ไขแล้วและวิธีการจัดวางกลับไปยังต้นไม้

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

ลองพิจารณาฟังก์ชัน 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 แรกเป็นโหนดใบ (Leaf Node) จึงรายงานขนาดและแสดงผลวิธีการจัดวางโฆษณา
  7. โหนด Column ขอให้โหนดย่อย Text รายการที่ 2 วัดค่า
  8. โหนด Text ที่ 2 เป็นโหนดใบ (Leaf Node) จึงรายงานขนาดและแสดงผลวิธีการจัดวางโฆษณา
  9. เมื่อโหนด Column วัดขนาด กำหนดขนาด และวางองค์ประกอบย่อยแล้ว ก็จะกำหนดขนาดและตำแหน่งของตัวเองได้
  10. เมื่อโหนดรูท Row วัดขนาด กำหนดขนาด และวางโหนดย่อยแล้ว ก็จะกำหนดขนาดและตำแหน่งของตัวเองได้

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

ประสิทธิภาพ

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

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

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

การใช้ตัวแก้ไขในเลย์เอาต์

ตามที่ได้พูดถึงในตัวปรับแต่ง Compose คุณสามารถใช้ตัวปรับแต่งเพื่อตกแต่งหรือเพิ่ม Composable ได้ ตัวแก้ไขเป็นสิ่งสำคัญ สำหรับการปรับแต่งเลย์เอาต์ ตัวอย่างเช่น เราเชื่อมโยงตัวแก้ไขหลายรายการที่นี่เพื่อปรับแต่ง 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 ตอบสนองต่อการป้อนข้อมูลของผู้ใช้และแสดงระลอกคลื่น
  • padding จะเว้นระยะห่างรอบองค์ประกอบ
  • fillMaxWidth ทำให้ Composable เติมความกว้างสูงสุดที่ได้รับจากองค์ประกอบหลัก
  • size() ระบุความกว้างและความสูงที่ต้องการขององค์ประกอบ

เลย์เอาต์แบบเลื่อนได้

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

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

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

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

ข้อจำกัด

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

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

แผนภาพแสดงช่องที่ใช้ได้ในแถบแอป Material Components

โดยปกติแล้ว Composable จะใช้แลมบ์ดา content Composable ( content: @Composable () -> Unit) Slot 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 ->
            // ...
        }
    }
}