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

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

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

คอมโพสิชันการเปลี่ยนสถานะเป็น UI ผ่านการจัดวาง เลย์เอาต์ ภาพวาด

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

เป้าหมายของเลย์เอาต์ในช่องการเขียน

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

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

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

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

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

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

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

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

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

ลองพิจารณาฟังก์ชัน 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 ของผลการค้นหา

ประสิทธิภาพ

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

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

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

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

ดูรายการและรายการแบบเลื่อนในเอกสารประกอบเกี่ยวกับการสร้างรายการ

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

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

ข้อจำกัด

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

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

เลย์เอาต์ตามช่อง

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

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

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

โดยปกติแล้วคอมโพสิเบิลจะใช้ content composable lambda ( 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 ->
            // ...
        }
    }
}