ตัวแก้ไขช่วยให้คุณตกแต่งหรือเพิ่มฟังก์ชันให้กับ Composable ได้ ตัวแก้ไขช่วยให้คุณทำสิ่งต่อไปนี้ได้
- เปลี่ยนขนาด เลย์เอาต์ ลักษณะการทำงาน และลักษณะที่ปรากฏของ Composable
- เพิ่มข้อมูล เช่น ป้ายกำกับการช่วยเหลือพิเศษ
- ประมวลผลอินพุตของผู้ใช้
- เพิ่มการโต้ตอบระดับสูง เช่น การทําให้องค์ประกอบคลิกได้ เลื่อนได้ ลากได้ หรือซูมได้
ตัวแก้ไขเป็นออบเจ็กต์ Kotlin มาตรฐาน สร้างตัวแก้ไขโดยเรียกใช้ฟังก์ชันคลาสใดคลาสหนึ่งต่อไปนี้
Modifier
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
คุณสามารถเชื่อมโยงฟังก์ชันเหล่านี้เข้าด้วยกันเพื่อสร้างฟังก์ชันใหม่ได้โดยทำดังนี้
@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, Elements accept and respect a
Modifier parameter
ลำดับของตัวแก้ไขมีความสำคัญ
ลำดับของฟังก์ชันตัวแก้ไขมีความสำคัญ เนื่องจากแต่ละฟังก์ชันจะทำการเปลี่ยนแปลงค่าที่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
ที่ใช้แลมบ์ดา
ดูข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับเวลาที่ควรใช้แต่ละรายการและวิธีเพิ่มประสิทธิภาพ
เพื่อประสิทธิภาพได้ในส่วนประสิทธิภาพของ Compose - เลื่อนการอ่านให้นานที่สุด
ขอบเขตความปลอดภัยใน Compose
ใน Compose มีตัวแก้ไขที่ใช้ได้เฉพาะเมื่อใช้กับองค์ประกอบย่อย ของ Composable บางรายการ Compose บังคับใช้สิ่งนี้ผ่านขอบเขตที่กำหนดเอง
เช่น หากต้องการให้องค์ประกอบย่อยมีขนาดเท่ากับองค์ประกอบหลักBox
โดยไม่Box
ส่งผลต่อขนาด ให้ใช้ตัวแก้ไข
matchParentSize
matchParentSize
จะมีเฉพาะใน
BoxScope
ดังนั้นจึงใช้ได้เฉพาะกับบุตรหลานภายในบัญชีผู้ปกครอง Box
ความปลอดภัยของขอบเขตช่วยป้องกันไม่ให้คุณเพิ่มตัวแก้ไขที่จะใช้ไม่ได้ใน Composable และขอบเขตอื่นๆ รวมถึงช่วยประหยัดเวลาจากการลองผิดลองถูก
ตัวแก้ไขที่กำหนดขอบเขตจะแจ้งให้ผู้ปกครองทราบเกี่ยวกับข้อมูลบางอย่างที่ผู้ปกครอง ควรรู้เกี่ยวกับบุตรหลาน ซึ่งมักเรียกกันว่าตัวแก้ไขข้อมูลหลัก การทำงานภายในของตัวแก้ไขเหล่านี้แตกต่างจากตัวแก้ไขอเนกประสงค์ แต่ในมุมมองการใช้งาน ความแตกต่างเหล่านี้ไม่สำคัญ
matchParentSize
ในBox
ดังที่กล่าวไว้ข้างต้น หากต้องการให้เลย์เอาต์ย่อยมีขนาดเท่ากับเลย์เอาต์หลัก
Box
โดยไม่ส่งผลต่อBox
ขนาด ให้ใช้ตัวแก้ไข matchParentSize
โปรดทราบว่า matchParentSize
จะใช้ได้ภายในขอบเขต Box
เท่านั้น ซึ่งหมายความว่า
จะมีผลกับองค์ประกอบที่ใช้ได้ของ Box
ที่เป็นองค์ประกอบย่อยโดยตรงเท่านั้น
ในตัวอย่างด้านล่าง Spacer
จะรับขนาดจากองค์ประกอบระดับบน Box
ซึ่งจะรับขนาดจากองค์ประกอบย่อยที่ใหญ่ที่สุด
ArtistCard
ในกรณีนี้
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
หากใช้ fillMaxSize
แทน matchParentSize
Spacer
จะใช้
พื้นที่ว่างทั้งหมดที่อนุญาตให้แก่องค์ประกอบหลัก ซึ่งจะทำให้องค์ประกอบหลัก
ขยายและเติมพื้นที่ว่างทั้งหมด
weight
ใน Row
และ Column
ดังที่ได้เห็นในส่วนก่อนหน้าเกี่ยวกับระยะขอบและ
ขนาด โดยค่าเริ่มต้น ระบบจะกำหนดขนาดที่ประกอบได้ตามเนื้อหาที่ห่อหุ้มอยู่ คุณสามารถตั้งค่าขนาดของ Composable ให้ยืดหยุ่นภายใน
องค์ประกอบระดับบนได้โดยใช้ weight
Modifier ซึ่งใช้ได้เฉพาะใน RowScope
และ
ColumnScope
มาดู Row
ที่มี Composable 2 รายการ Box
กัน
กล่องแรกมี weight
เป็น 2 เท่าของกล่องที่ 2 จึงมี
ความกว้างเป็น 2 เท่า เนื่องจาก Row
กว้าง 210.dp
ดังนั้น Box
แรกจึงกว้าง 140.dp
และ
70.dp
ที่ 2 มีขนาดดังนี้
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
การแยกและนำตัวแก้ไขกลับมาใช้ใหม่
คุณสามารถเชื่อมโยงตัวแก้ไขหลายรายการเข้าด้วยกันเพื่อตกแต่งหรือ
เพิ่มฟังก์ชันให้กับ Composable เชนนี้สร้างขึ้นผ่านอินเทอร์เฟซ Modifier
ซึ่งแสดงรายการที่เรียงลำดับและเปลี่ยนแปลงไม่ได้ของ Modifier.Elements
รายการเดียว
แต่ละ Modifier.Element
แสดงถึงลักษณะการทำงานแต่ละอย่าง เช่น เลย์เอาต์ การวาด
และลักษณะการทำงานของกราฟิก ลักษณะการทำงานที่เกี่ยวข้องกับท่าทางสัมผัสทั้งหมด ลักษณะการทำงานของโฟกัสและความหมาย รวมถึง
เหตุการณ์อินพุตของอุปกรณ์ ลำดับขององค์ประกอบตัวแก้ไขมีความสำคัญ โดยองค์ประกอบตัวแก้ไขที่เพิ่มก่อนจะได้รับการใช้ก่อน
บางครั้งการนำอินสแตนซ์ของเชนตัวแก้ไขเดียวกันมาใช้ซ้ำใน Composable หลายรายการอาจเป็นประโยชน์ โดยการแยกอินสแตนซ์เหล่านั้นเป็นตัวแปรและย้ายไปยังขอบเขตที่สูงขึ้น ซึ่งจะช่วยปรับปรุงความสามารถในการอ่านโค้ดหรือช่วยปรับปรุงประสิทธิภาพของแอปได้ด้วยเหตุผลต่อไปนี้
- การจัดสรรตัวแก้ไขใหม่จะไม่เกิดขึ้นซ้ำเมื่อมีการจัดองค์ประกอบใหม่ สำหรับ Composable ที่ใช้ตัวแก้ไข
- ห่วงโซ่ตัวแก้ไขอาจยาวและซับซ้อนมาก ดังนั้นการนำอินสแตนซ์ของห่วงโซ่เดียวกันกลับมาใช้ใหม่จะช่วยลดภาระงานที่รันไทม์ของ Compose ต้องทำเมื่อเปรียบเทียบ
- การแยกนี้ช่วยส่งเสริมความสะอาด ความสอดคล้อง และความสามารถในการบำรุงรักษาโค้ด ในโค้ดเบส
แนวทางปฏิบัติแนะนำสำหรับการใช้ตัวแก้ไขซ้ำ
สร้างเชน Modifier
ของคุณเองและแยกเชนเหล่านั้นเพื่อนำไปใช้ซ้ำในคอมโพเนนต์ที่ประกอบได้หลายรายการ
คุณจะบันทึกเฉพาะตัวแก้ไขก็ได้ เนื่องจากตัวแก้ไขเป็นออบเจ็กต์ที่คล้ายข้อมูล
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
การแยกและนำตัวแก้ไขกลับมาใช้ใหม่เมื่อสังเกตสถานะที่มีการเปลี่ยนแปลงบ่อย
เมื่อสังเกตสถานะที่เปลี่ยนแปลงบ่อยภายใน Composable เช่น สถานะของภาพเคลื่อนไหวหรือ scrollState
อาจมีการ Recomposition จำนวนมาก
เกิดขึ้น ในกรณีนี้ ระบบจะจัดสรรตัวปรับของคุณในการจัดองค์ประกอบใหม่ทุกครั้ง
และอาจจัดสรรให้กับทุกเฟรม
@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 ) }
ซึ่งจะมีประโยชน์อย่างยิ่งเมื่อใช้ร่วมกับเลย์เอาต์แบบเลซี่ ในกรณีส่วนใหญ่ คุณคงต้องการให้สินค้าทั้งหมดที่มีจำนวนมากและอาจมีความสำคัญ มีตัวแก้ไขเหมือนกันทุกประการ
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 // ... ) // ... }
คุณควรส่งเฉพาะตัวแก้ไขที่ขอบเขตเดียวกันและตัวแก้ไขที่ขอบเขตที่แยกออกมาไปยังองค์ประกอบย่อยโดยตรงที่มีขอบเขตเดียวกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่เรื่องนี้มีความสำคัญได้ที่ส่วนความปลอดภัยของขอบเขตใน Compose
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)
โปรดทราบว่าลำดับของตัวแก้ไขมีความสำคัญ
ดูข้อมูลเพิ่มเติม
เรามีรายการตัวแก้ไขทั้งหมดพร้อมพารามิเตอร์และขอบเขต
หากต้องการฝึกวิธีใช้ตัวแก้ไขเพิ่มเติม คุณสามารถดู เลย์เอาต์พื้นฐานใน Codelab ของ Compose หรือดู ที่เก็บ Now in Android
ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวแก้ไขที่กำหนดเองและวิธีสร้างได้ที่เอกสารประกอบเกี่ยวกับเลย์เอาต์ที่กำหนดเอง - การใช้ตัวแก้ไขเลย์เอาต์
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ข้อมูลเบื้องต้นเกี่ยวกับเลย์เอาต์การเขียน
- การดำเนินการของเอดิเตอร์ {:#editor-actions}
- เลย์เอาต์ที่กำหนดเอง {:#custom-layouts }