ตัวปรับแต่งการเขียน

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

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

ตัวปรับแต่งคือวัตถุ Kotlin มาตรฐาน สร้างเครื่องปรับโดยเรียกหนึ่งใน ฟังก์ชันของชั้นเรียน Modifier

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

ข้อความ 2 บรรทัดบนพื้นหลังสี โดยมีระยะห่างจากขอบรอบข้อความ

คุณเชื่อมโยงฟังก์ชันเหล่านี้เข้าด้วยกันเพื่อเขียนฟังก์ชันได้

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

พื้นหลังที่เป็นสีด้านหลังข้อความจะขยายเต็มความกว้างของอุปกรณ์

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

  • padding วางพื้นที่รอบๆ องค์ประกอบ
  • fillMaxWidth ทำให้ Composable เติมความกว้างสูงสุดตามที่กำหนดไว้จาก ระดับบนสุด

แนวทางปฏิบัติแนะนำคือให้ Composable ทั้งหมดยอมรับ modifier แล้วส่งตัวแก้ไขนั้นไปยังหน่วยย่อยรายแรกที่แสดง UI การทำเช่นนี้ทำให้ ทำให้สามารถนำมาใช้งานซ้ำได้มากขึ้น รวมถึงทำให้ลักษณะการทำงานสามารถคาดเดาได้และเข้าใจง่ายยิ่งขึ้น สำหรับ ดูข้อมูลเพิ่มเติมได้ในหลักเกณฑ์ของ Compose API, องค์ประกอบต่างๆ ยอมรับและดำเนินการตาม พารามิเตอร์ตัวปรับแต่ง

ลำดับของคีย์ตัวปรับแต่งมีความสำคัญ

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

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

พื้นที่ทั้งหมด ซึ่งรวมถึงระยะห่างจากขอบรอบขอบต่างๆ ที่ตอบสนองต่อการคลิก

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

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

ระยะห่างจากขอบของเลย์เอาต์ไม่ตอบสนองต่อการคลิกอีกต่อไป

ตัวแก้ไขในตัว

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

padding และ size

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

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

โปรดทราบว่าระบบอาจไม่ยึดตามขนาดที่คุณระบุหากไม่เป็นไปตาม ข้อจำกัดที่มาจากโฆษณาหลักของเลย์เอาต์ ถ้าคุณต้องการใช้ Composable ขนาดที่จะแก้ไขโดยไม่คำนึงถึงข้อจำกัดขาเข้า ให้ใช้ requiredSize ตัวปรับแต่ง:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

รูปภาพย่อยมีขนาดใหญ่กว่าข้อจำกัดที่มาจากองค์ประกอบหลัก

ในตัวอย่างนี้ แม้ว่าตั้งค่า height ระดับบนสุดเป็น 100.dp ความสูงของ Image จะเป็น 150.dp เนื่องจากใช้ตัวแก้ไข requiredSize ลำดับความสำคัญ

ถ้าคุณต้องการให้เค้าโครงย่อยเติมเต็มความสูงที่ใช้ได้ทั้งหมดซึ่ง ระดับบน ให้เพิ่มแป้นกดร่วม fillMaxHeight (Compose จะมี fillMaxSize และ fillMaxWidth):

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

ความสูงของรูปภาพใหญ่เท่ากับขนาดระดับบนสุด

หากต้องการเพิ่มระยะห่างจากขอบรอบๆ องค์ประกอบ ให้ตั้งค่าตัวแก้ไข padding

ถ้าต้องการเพิ่มระยะห่างจากขอบเหนือข้อความพื้นฐาน ระยะห่างที่เจาะจงจากด้านบนของเลย์เอาต์ไปยังเกณฑ์พื้นฐาน ตัวปรับแต่ง paddingFromBaseline:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

ข้อความที่มีระยะห่างจากขอบด้านบน

ออฟเซ็ต

หากต้องการวางตำแหน่งเลย์เอาต์ที่สัมพันธ์กับตำแหน่งเดิม ให้เพิ่มแอตทริบิวต์ offset ตัวแก้ไขและตั้งค่าออฟเซ็ตในแกน x และ y ออฟเซ็ตอาจเป็นได้ทั้งเชิงบวกและไม่ใช่เชิงบวก ความแตกต่างระหว่าง padding และ offset คือการเพิ่ม offset ลงใน Composable ไม่ใช่ เปลี่ยนการวัดค่า:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

ย้ายข้อความไปทางด้านขวาของที่เก็บข้อความระดับบนสุดแล้ว

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

ตัวแก้ไข offset มีโอเวอร์โหลด 2 รายการ คือ offset ที่ใช้เวลา ออฟเซ็ตเป็นพารามิเตอร์และ offset ที่ใช้ lambda หากต้องการข้อมูลเชิงลึกเพิ่มเติมว่าควรใช้โซลูชันเหล่านี้เมื่อใด และวิธีเพิ่มประสิทธิภาพ สำหรับประสิทธิภาพ โปรดอ่าน ประสิทธิภาพการเขียน - เลื่อนการอ่านให้นานที่สุด

กำหนดขอบเขตความปลอดภัยของการเขียน

ใน Compose มีตัวแก้ไขที่ใช้ได้เฉพาะเมื่อมีผลกับรายการย่อยเท่านั้น Composable บางรายการ Compose จะบังคับใช้ขอบเขตนี้โดยใช้ขอบเขตที่กำหนดเอง

เช่น หากต้องการกำหนดให้บุตรหลานใหญ่เท่ากับบัญชีหลักBoxโดยไม่มี ส่งผลต่อขนาด Box ให้ใช้ matchParentSize แป้นกดร่วม matchParentSize พร้อมให้ใช้งานเฉพาะใน BoxScope ดังนั้นจึงใช้ได้กับรายการย่อยภายใน Box ระดับบนสุดเท่านั้น

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

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

matchParentSizeในBox

ตามที่กล่าวไว้ข้างต้น หากคุณต้องการให้เลย์เอาต์ย่อยมีขนาดเท่ากับหน่วยโฆษณาหลัก Box โดยไม่ส่งผลต่อขนาด Box ให้ใช้ตัวแก้ไข matchParentSize

โปรดทราบว่า matchParentSize พร้อมใช้งานภายในขอบเขต Box เท่านั้น ซึ่งหมายความว่า ก็จะใช้ได้เฉพาะกับองค์ประกอบย่อยโดยตรงของ Composable Box

ในตัวอย่างด้านล่าง ย่อย Spacer นำขนาดมาจาก Box ระดับบน ซึ่งนำขนาดมาจากเด็กๆ ที่โตที่สุด ในกรณีนี้คือ ArtistCard

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

พื้นหลังสีเทาแสดงสิ่งที่อยู่ในภาชนะ

หากใช้ fillMaxSize แทน matchParentSize ระบบจะใช้ Spacer พื้นที่ว่างทั้งหมดที่ผู้ปกครองอนุญาต ซึ่งจะส่งผลให้ผู้ปกครอง ขยายและเติมพื้นที่ว่างทั้งหมดที่มีอยู่

พื้นหลังสีเทาเต็มหน้าจอ

weightในRowและColumn

อย่างที่คุณได้เห็นในส่วนก่อนหน้านี้เกี่ยวกับระยะห่างจากขอบและ size [ขนาด] โดยค่าเริ่มต้น ขนาด Composable จะกำหนดโดยแอตทริบิวต์ เนื้อหาที่ห่อหุ้มอยู่ คุณสามารถตั้งค่าขนาด Composable ให้ยืดหยุ่นภายใน ผู้ปกครองที่ใช้ตัวปรับแต่ง weight ที่มีเฉพาะใน RowScope และ ColumnScope

ลองพิจารณา Row ที่มี Composable Box 2 รายการ ช่องแรกมีสองเท่าของ weight ของช่องที่สอง ดังนั้นช่องนี้จึงได้รับสองเท่าของ ความกว้าง เนื่องจาก Row กว้าง 210.dp ค่า Box แรกจึงกว้าง 140.dp และ รายการที่ 2 คือ 70.dp

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

ความกว้างของรูปภาพเท่ากับความกว้างของข้อความเป็น 2 เท่า

การแยกและนำตัวปรับแต่งมาใช้ใหม่

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

Modifier.Element แต่ละรายการจะแสดงลักษณะการทำงานแต่ละรายการ เช่น เลย์เอาต์ ภาพวาด และพฤติกรรมของกราฟิก พฤติกรรมที่เกี่ยวข้องกับท่าทางสัมผัส การโฟกัส และความหมาย รวมถึงเหตุการณ์อินพุตของอุปกรณ์ การจัดลำดับความสำคัญขององค์ประกอบเหล่านี้: องค์ประกอบที่มีตัวแก้ไขที่ ที่เพิ่มเข้ามาก่อน

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

  • ระบบจะไม่ทำซ้ำการจัดสรรตัวแก้ไขใหม่เมื่อจัดองค์ประกอบใหม่ Composable ที่ใช้คอมโพเนนต์
  • ห่วงโซ่ตัวปรับแต่งอาจยาวและซับซ้อนมาก ดังนั้นการนำโค้ด อินสแตนซ์เดียวกันของเชนจะช่วยลดภาระงานที่รันไทม์ของ Compose ต้องทำได้ เมื่อเปรียบเทียบ
  • การแยกออกนี้จะสนับสนุนความสะอาดของโค้ด ความสอดคล้อง และการบำรุงรักษา ในฐานของโค้ด

แนวทางปฏิบัติแนะนำในการใช้ตัวแก้ไขซ้ำ

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

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

การแยกและนำตัวปรับไปใช้ซ้ำเมื่อสังเกตสถานะที่เปลี่ยนแปลงบ่อย

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

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

แต่คุณสามารถสร้าง แยก และนำอินสแตนซ์ของตัวแก้ไขเดิมมาใช้ซ้ำได้ และส่งไปยัง Composable ดังนี้

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

การแตกข้อมูลและนำตัวแก้ไขที่ไม่มีขอบเขตมาใช้ซ้ำ

ตัวปรับแต่งอาจยกเลิกการกำหนดขอบเขตหรือกำหนดขอบเขตเฉพาะ Composable ในกรณีของตัวแก้ไขที่ไม่กำหนดขอบเขต คุณสามารถแยกแป้นเหล่านั้นออกได้ง่ายๆ ภายนอก Composable ใดๆ เป็นตัวแปรพื้นฐาน:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

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

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

การแตกข้อมูลและนำตัวปรับแต่งที่กำหนดขอบเขตไปใช้ซ้ำ

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

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

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

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

การผูกมัดมากขึ้นของตัวแก้ไขที่แยกออกมา

คุณสามารถเพิ่มเชนเพิ่มเติมหรือเพิ่มเชนตัวปรับแต่งที่ดึงข้อมูลได้โดยเรียกเมธอด ฟังก์ชัน .then():

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

แต่อย่าลืมว่าลำดับของตัวแก้ไขมีความสำคัญ

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

เรามีรายการตัวปรับแต่งทั้งหมด พร้อมด้วย พารามิเตอร์และขอบเขต

หากต้องการฝึกฝนเพิ่มเติมเกี่ยวกับวิธีใช้ตัวแก้ไข โปรดดู เลย์เอาต์พื้นฐานใน Compose Codelab หรือดูที่ อยู่ในที่เก็บ Android แล้ว

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