Compose จะแสดงผลเฟรมผ่านระยะต่างๆ ที่แยกกัน เช่นเดียวกับเครื่องมือชุด UI อื่นๆ ส่วนใหญ่ เมื่อดูที่ระบบ View ของ Android จะมี 3 ระยะหลัก ได้แก่ การวัด เลย์เอาต์ และการวาด คอมโพสิชันคล้ายกันมาก แต่มีขั้นตอนเพิ่มเติมที่สำคัญที่เรียกว่าคอมโพสิชันในช่วงเริ่มต้น
เราได้อธิบายการคอมโพสิชันไว้ในเอกสาร Compose ซึ่งรวมถึงการคิดใน Compose และState และ Jetpack Compose
เฟรมแบ่งออกเป็น 3 ช่วง
การสร้างมี 3 ระยะหลักดังนี้
- การคอมโพส: UI ที่จะแสดง Compose จะเรียกใช้ฟังก์ชันที่คอมโพสิเบิลและสร้างคำอธิบาย UI
- เลย์เอาต์: ตําแหน่งที่จะวาง UI ระยะนี้ประกอบด้วย 2 ขั้นตอน ได้แก่ การวัดผลและตำแหน่ง องค์ประกอบเลย์เอาต์จะวัดและวางองค์ประกอบย่อยและตัวมันเองในพิกัด 2 มิติสำหรับแต่ละโหนดในต้นไม้เลย์เอาต์
- การวาด: วิธีแสดงผล องค์ประกอบ UI จะวาดลงใน Canvas ซึ่งโดยปกติจะเป็นหน้าจออุปกรณ์
โดยทั่วไปลําดับของระยะเหล่านี้จะเหมือนกัน ซึ่งช่วยให้ข้อมูลไหลไปในทิศทางเดียวจากการจัดองค์ประกอบไปยังเลย์เอาต์ไปยังการวาดเพื่อสร้างเฟรม (หรือที่เรียกว่าการไหลของข้อมูลแบบทิศทางเดียว)
BoxWithConstraints
และ LazyColumn
และ LazyRow
เป็นข้อยกเว้นที่สำคัญ ซึ่งองค์ประกอบขององค์ประกอบย่อยจะขึ้นอยู่กับระยะของเลย์เอาต์ขององค์ประกอบหลัก
ในทางแนวคิด แต่ละระยะเหล่านี้จะเกิดขึ้นกับทุกเฟรม แต่ Compose จะหลีกเลี่ยงการทำงานซ้ำที่จะคำนวณผลลัพธ์เดียวกันจากอินพุตเดียวกันในทุกระยะเหล่านี้เพื่อเพิ่มประสิทธิภาพ คอมโพซจะข้ามการเรียกใช้ฟังก์ชันคอมโพสิเบิลหากใช้ผลลัพธ์ก่อนหน้าซ้ำได้ และ UI คอมโพซจะไม่จัดวางใหม่หรือวาดต้นไม้ทั้งต้นอีกครั้งหากไม่จำเป็น คอมโพซจะทํางานเพียงเท่าที่จำเป็นในการอัปเดต UI การเพิ่มประสิทธิภาพนี้เป็นไปได้เนื่องจาก Compose คอยติดตามการอ่านสถานะภายในเฟสต่างๆ
ทําความเข้าใจระยะต่างๆ
ส่วนนี้จะอธิบายการดำเนินการของระยะการทำงาน 3 ระยะของ Compose สำหรับคอมโพสิเบิลอย่างละเอียดยิ่งขึ้น
การเรียบเรียง
ในขั้นตอนการคอมโพสิชัน รันไทม์ Compose จะเรียกใช้ฟังก์ชันที่คอมโพสิชันได้ และแสดงผลโครงสร้างต้นไม้ที่แสดง UI ต้นไม้ UI นี้ประกอบด้วยโหนดเลย์เอาต์ที่มีข้อมูลทั้งหมดที่จำเป็นสำหรับระยะถัดไป ดังที่แสดงในวิดีโอต่อไปนี้
รูปที่ 2 ต้นไม้ที่แสดง UI ที่สร้างขึ้นในขั้นตอนการคอมโพสิชัน
ส่วนย่อยของโค้ดและต้นไม้ UI มีลักษณะดังต่อไปนี้
ในตัวอย่างนี้ ฟังก์ชันคอมโพสิเบิลแต่ละรายการในโค้ดจะแมปกับโหนดเลย์เอาต์เดียวในต้นไม้ UI ในตัวอย่างที่ซับซ้อนมากขึ้น คอมโพสิเบิลอาจมีตรรกะและโฟลว์การควบคุม รวมถึงสร้างต้นไม้ที่แตกต่างกันตามสถานะต่างๆ
เลย์เอาต์
ในขั้นตอนการวางเลย์เอาต์ Compose จะใช้ต้นไม้ UI ที่สร้างขึ้นในระยะการคอมโพสิชันเป็นอินพุต ชุดโหนดเลย์เอาต์มีข้อมูลทั้งหมดที่จําเป็นสําหรับการกําหนดขนาดและตําแหน่งของโหนดแต่ละโหนดในพื้นที่ 2 มิติ
รูปที่ 4 การวัดผลและตำแหน่งของโหนดเลย์เอาต์แต่ละโหนดในต้นไม้ UI ในระหว่างระยะการวางเลย์เอาต์
ในระหว่างระยะการจัดวาง ระบบจะไปยังส่วนต่างๆ ของต้นไม้โดยใช้อัลกอริทึม 3 ขั้นตอนต่อไปนี้
- วัดโหนดย่อย: โหนดจะวัดโหนดย่อย (หากมี)
- กำหนดขนาดของตนเอง: โหนดจะกำหนดขนาดของตนเองโดยอิงตามการวัดเหล่านี้
- วางรายการย่อย: แต่ละโหนดย่อยจะวางตามตำแหน่งของโหนดนั้นๆ
เมื่อสิ้นสุดระยะนี้ โหนดเลย์เอาต์แต่ละโหนดจะมีข้อมูลต่อไปนี้
- ความกว้างและความสูงที่กำหนด
- พิกัด x, y ที่ควรวาด
โปรดจำโครงสร้าง UI จากส่วนก่อนหน้า
สําหรับต้นไม้นี้ อัลกอริทึมจะทํางานดังนี้
Row
จะวัดค่าของImage
และColumn
- ระบบจะวัด
Image
โหนดนี้ไม่มีโหนดย่อย จึงตัดสินใจเลือกขนาดของตนเองและรายงานขนาดกลับไปยังRow
- ระบบจะวัด
Column
ในลำดับถัดไป โดยจะวัดค่าองค์ประกอบของตัวเอง (Text
คอมโพสิเบิล 2 รายการ) ก่อน - ระบบจะวัด
Text
รายการแรก โหนดนี้ไม่มีโหนดย่อย จึงกำหนดขนาดของตัวเองและรายงานขนาดกลับไปยังColumn
- วัด
Text
ที่ 2 โหนดนี้ไม่มีโหนดย่อย จึงตัดสินใจเลือกขนาดของตัวเองและรายงานกลับไปที่Column
- วัด
Column
ใช้การวัดขององค์ประกอบย่อยเพื่อกำหนดขนาดของตนเอง โดยจะใช้ความกว้างสูงสุดของรายการย่อยและผลรวมของความสูงของรายการย่อยColumn
จะวางองค์ประกอบย่อยตามความสัมพันธ์กับตัวเอง โดยวางไว้ใต้กันแนวตั้งRow
ใช้การวัดขององค์ประกอบย่อยเพื่อกำหนดขนาดของตนเอง โดยจะใช้ความสูงสูงสุดขององค์ประกอบย่อยและผลรวมของความกว้างขององค์ประกอบย่อย จากนั้นจึงวางบรรทัดย่อย
โปรดทราบว่ามีการเข้าชมโหนดแต่ละโหนดเพียงครั้งเดียว รันไทม์ Compose ต้องการการผ่านผ่านต้นไม้ UI เพียงครั้งเดียวเพื่อวัดและวางโหนดทั้งหมด ซึ่งจะช่วยเพิ่มประสิทธิภาพ เมื่อจํานวนโหนดในต้นไม้เพิ่มขึ้น เวลาที่ใช้สําหรับการเรียกดูจะเพิ่มขึ้นตามลําดับเชิงเส้น ในทางตรงกันข้าม หากมีการเข้าชมโหนดแต่ละโหนดหลายครั้ง เวลาในการเข้าชมจะเพิ่มขึ้นแบบทวีคูณ
ภาพวาด
ในขั้นตอนการวาด ระบบจะวนผ่านต้นไม้อีกครั้งจากบนลงล่าง และแต่ละโหนดจะวาดตัวเองบนหน้าจอตามลำดับ
รูปที่ 5 ระยะการวาดจะวาดพิกเซลบนหน้าจอ
เมื่อใช้ตัวอย่างก่อนหน้านี้ ระบบจะวาดเนื้อหาแผนภูมิต้นไม้ดังนี้
Row
จะวาดเนื้อหาทั้งหมดที่อาจมี เช่น สีพื้นหลังImage
จะวาดเองColumn
จะวาดเองText
ตัวแรกและตัวที่ 2 จะวาดตัวเองตามลำดับ
รูปที่ 6 ต้นไม้ UI และการแสดงภาพ
การอ่านสถานะ
เมื่อคุณอ่านค่าของสถานะภาพรวมในช่วงใดช่วงหนึ่งที่ระบุไว้ข้างต้น Compose จะติดตามสิ่งที่ทําอยู่โดยอัตโนมัติเมื่ออ่านค่า การติดตามนี้ช่วยให้ Compose เรียกใช้โปรแกรมอ่านอีกครั้งได้เมื่อค่าสถานะเปลี่ยนแปลง และเป็นพื้นฐานของการสังเกตสถานะใน Compose
โดยทั่วไปแล้ว สถานะจะสร้างขึ้นโดยใช้ mutableStateOf()
จากนั้นเข้าถึงได้ 2 วิธี ได้แก่ เข้าถึงพร็อพเพอร์ตี้ value
โดยตรง หรือใช้ตัวรับช่วงพร็อพเพอร์ตี้ Kotlin อ่านข้อมูลเพิ่มเติมเกี่ยวกับสถานะได้ในสถานะในคอมโพสิเบิล "การอ่านสถานะ" หมายถึงวิธีการเข้าถึงที่เทียบเท่ากันวิธีใดวิธีหนึ่งสำหรับวัตถุประสงค์ของคู่มือนี้
// State read without property delegate. val paddingState: MutableState<Dp> = remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.padding(paddingState.value) )
// State read with property delegate. var padding: Dp by remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.padding(padding) )
เบื้องหลังของ property
delegate จะมีการใช้ฟังก์ชัน "getter" และ "setter" เพื่อเข้าถึงและอัปเดตvalue
ของรัฐ ฟังก์ชัน getter และ setter เหล่านี้จะเรียกใช้เฉพาะเมื่อคุณอ้างอิงพร็อพเพอร์ตี้เป็นค่าเท่านั้น ไม่ใช่เมื่อสร้างพร็อพเพอร์ตี้ขึ้น ด้วยเหตุนี้ 2 วิธีข้างต้นจึงเทียบเท่ากัน
แต่ละบล็อกโค้ดที่เรียกใช้ซ้ำได้เมื่อสถานะการอ่านมีการเปลี่ยนแปลงคือขอบเขตการเริ่มใหม่ คอมโพซจะติดตามการเปลี่ยนแปลงค่าสถานะและรีสตาร์ทขอบเขตในแต่ละระยะ
การอ่านสถานะแบบเป็นระยะ
ดังที่กล่าวไว้ข้างต้น คอมโพซมี 3 ระยะหลัก และคอมโพซจะติดตามสถานะใดภายในแต่ละระยะ วิธีนี้ช่วยให้ Compose แจ้งเฉพาะระยะที่เฉพาะเจาะจงซึ่งต้องดำเนินการกับองค์ประกอบ UI แต่ละรายการที่ได้รับผลกระทบ
มาดูแต่ละระยะและอธิบายสิ่งที่จะเกิดขึ้นเมื่ออ่านค่าสถานะภายในระยะนั้น
ระยะที่ 1: การจัดองค์ประกอบ
การอ่านสถานะภายใน@Composable
ฟังก์ชันหรือบล็อก Lambda จะส่งผลต่อองค์ประกอบ และอาจส่งผลต่อระยะถัดไปด้วย เมื่อค่าสถานะเปลี่ยนแปลง เครื่องมือจัดเรียงใหม่จะกำหนดเวลาให้ฟังก์ชันคอมโพสิเบิลทั้งหมดที่อ่านค่าสถานะนั้นทำงานอีกครั้ง โปรดทราบว่ารันไทม์อาจเลือกที่จะข้ามฟังก์ชันแบบคอมโพสิเบิลบางส่วนหรือทั้งหมดหากอินพุตไม่มีการเปลี่ยนแปลง ดูข้อมูลเพิ่มเติมได้ที่การข้ามหากอินพุตไม่มีการเปลี่ยนแปลง
UI ของ Compose จะเรียกใช้ระยะการวางเลย์เอาต์และการวาด ทั้งนี้ขึ้นอยู่กับผลลัพธ์ของการจัดองค์ประกอบ ระบบอาจข้ามระยะเหล่านี้หากเนื้อหายังคงเหมือนเดิม และขนาดและเลย์เอาต์จะไม่เปลี่ยนแปลง
var padding by remember { mutableStateOf(8.dp) } Text( text = "Hello", // The `padding` state is read in the composition phase // when the modifier is constructed. // Changes in `padding` will invoke recomposition. modifier = Modifier.padding(padding) )
ระยะที่ 2: เลย์เอาต์
ระยะการวางผังประกอบด้วย 2 ขั้นตอน ได้แก่ การวัดและตำแหน่ง ขั้นตอนการวัดจะเรียกใช้ Lambda การวัดที่ส่งไปยัง Layout
composable, MeasureScope.measure
เมธอดของอินเทอร์เฟซ LayoutModifier
และอื่นๆ ขั้นตอนตำแหน่งจะเรียกใช้บล็อกตำแหน่งของฟังก์ชัน layout
, บล็อก Lambda ของ Modifier.offset { … }
และอื่นๆ
การอ่านสถานะในแต่ละขั้นตอนเหล่านี้จะส่งผลต่อเลย์เอาต์และอาจส่งผลต่อระยะการวาด เมื่อค่าสถานะเปลี่ยนแปลง Compose UI จะกำหนดเวลาระยะการวางเลย์เอาต์ และยังเรียกใช้ระยะการวาดด้วยหากขนาดหรือตำแหน่งมีการเปลี่ยนแปลง
กล่าวอย่างละเอียดคือ ขั้นตอนการวัดและขั้นตอนตําแหน่งมีขอบเขตการเริ่มใหม่แยกกัน ซึ่งหมายความว่าการอ่านสถานะในขั้นตอนตําแหน่งจะไม่เรียกใช้ขั้นตอนการวัดก่อนหน้านั้นอีกครั้ง อย่างไรก็ตาม ขั้นตอนทั้ง 2 ขั้นตอนนี้มักจะเกี่ยวพันกัน สถานะที่อ่านในขั้นตอนตําแหน่งจึงอาจส่งผลต่อขอบเขตการเริ่มใหม่อื่นๆ ที่อยู่ในขั้นตอนการวัด
var offsetX by remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.offset { // The `offsetX` state is read in the placement step // of the layout phase when the offset is calculated. // Changes in `offsetX` restart the layout. IntOffset(offsetX.roundToPx(), 0) } )
ระยะที่ 3: การวาด
การอ่านสถานะระหว่างโค้ดการวาดจะส่งผลต่อระยะการวาด ตัวอย่างทั่วไป ได้แก่ Canvas()
, Modifier.drawBehind
และ Modifier.drawWithContent
เมื่อค่าสถานะเปลี่ยนแปลง Compose UI จะเรียกใช้เฉพาะระยะวาดเท่านั้น
var color by remember { mutableStateOf(Color.Red) } Canvas(modifier = modifier) { // The `color` state is read in the drawing phase // when the canvas is rendered. // Changes in `color` restart the drawing. drawRect(color) }
การเพิ่มประสิทธิภาพการอ่านสถานะ
เนื่องจาก Compose ทำการติดตามการอ่านสถานะที่แปลแล้ว เราจึงลดปริมาณงานที่ทําได้โดยอ่านสถานะแต่ละรายการในระยะที่เหมาะสม
มาดูตัวอย่างกัน ที่นี่เรามี Image()
ที่ใช้ตัวปรับเปลี่ยนออฟเซตเพื่อหักลบตำแหน่งเลย์เอาต์สุดท้าย ซึ่งทำให้เกิดเอฟเฟกต์ภาพพารัลแลกซ์เมื่อผู้ใช้เลื่อน
Box { val listState = rememberLazyListState() Image( // ... // Non-optimal implementation! Modifier.offset( with(LocalDensity.current) { // State read of firstVisibleItemScrollOffset in composition (listState.firstVisibleItemScrollOffset / 2).toDp() } ) ) LazyColumn(state = listState) { // ... } }
โค้ดนี้ใช้งานได้แต่ให้ประสิทธิภาพที่ไม่เหมาะสม ตามที่เขียนไว้ โค้ดจะอ่านค่าของสถานะ firstVisibleItemScrollOffset
และส่งค่านั้นให้กับฟังก์ชัน Modifier.offset(offset: Dp)
เมื่อผู้ใช้เลื่อนค่า firstVisibleItemScrollOffset
จะเปลี่ยนแปลง เราทราบดีว่า Compose จะติดตามสถานะการอ่านทั้งหมดเพื่อให้สามารถเริ่มอ่านโค้ดอีกครั้งได้ (เรียกใช้ใหม่) ซึ่งในตัวอย่างนี้คือเนื้อหาของ Box
นี่เป็นตัวอย่างสถานะที่อ่านภายในระยะการจัดองค์ประกอบ ซึ่งก็ไม่ได้เป็นเรื่องที่ไม่ดีเสมอไป และที่จริงแล้วเป็นพื้นฐานของการคอมโพสิชันใหม่ ซึ่งช่วยให้การเปลี่ยนแปลงข้อมูลสร้าง UI ใหม่ได้
ในตัวอย่างนี้ เหตุการณ์การเลื่อนแต่ละครั้งจะส่งผลให้ระบบประเมินเนื้อหาแบบคอมโพสิเบิลทั้งหมดอีกครั้ง จากนั้นจึงวัด จัดวาง และวาด เราเรียกใช้ระยะการเขียนทุกครั้งที่เลื่อน แม้ว่าสิ่งที่แสดงจะไม่เปลี่ยนแปลง แต่มีเพียงตำแหน่งที่แสดงเท่านั้น เราเพิ่มประสิทธิภาพการอ่านสถานะเพื่อทริกเกอร์ระยะเลย์เอาต์อีกครั้งได้เท่านั้น
ตัวแก้ไขระยะห่างอีกเวอร์ชันหนึ่งมีดังนี้
Modifier.offset(offset: Density.() -> IntOffset)
เวอร์ชันนี้ใช้พารามิเตอร์ LAMBDA โดยบล็อก LAMBDA จะแสดงผลออฟเซตที่ได้ มาอัปเดตโค้ดเพื่อใช้งานกัน
Box { val listState = rememberLazyListState() Image( // ... Modifier.offset { // State read of firstVisibleItemScrollOffset in Layout IntOffset(x = 0, y = listState.firstVisibleItemScrollOffset / 2) } ) LazyColumn(state = listState) { // ... } }
เหตุใดวิธีนี้จึงมีประสิทธิภาพมากกว่า ระบบจะเรียกใช้บล็อก Lambda ที่เราระบุให้กับตัวแก้ไขในระยะเลย์เอาต์ (โดยเฉพาะในขั้นตอนตำแหน่งของระยะเลย์เอาต์) ซึ่งหมายความว่าระบบจะไม่อ่านสถานะ firstVisibleItemScrollOffset
ในระหว่างการคอมโพสิชันอีกต่อไป เนื่องจาก Compose จะติดตามเมื่อมีการอ่านสถานะ การเปลี่ยนแปลงนี้จึงหมายความว่าหากค่า firstVisibleItemScrollOffset
เปลี่ยนแปลง Compose จะต้องเริ่มระยะการจัดวางและการวาดใหม่เท่านั้น
ตัวอย่างนี้ใช้ตัวแก้ไขออฟเซ็ตที่แตกต่างกันเพื่อเพิ่มประสิทธิภาพโค้ดที่ได้ แต่แนวคิดทั่วไปคือพยายามจำกัดการอ่านสถานะให้อยู่ในระยะต่ำที่สุดเท่าที่จะเป็นไปได้ ซึ่งจะช่วยให้ Compose ทำงานได้น้อยที่สุด
แน่นอนว่าในบางครั้งก็จำเป็นต้องอ่านสถานะในเฟสการคอมโพสิชัน อย่างไรก็ตาม ก็มีบางกรณีที่เราสามารถลดจำนวนการสร้างใหม่ได้โดยกรองการเปลี่ยนแปลงสถานะ ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่ derivedStateOf: แปลงออบเจ็กต์สถานะอย่างน้อย 1 รายการเป็นสถานะอื่น
ลูปการจัดองค์ประกอบใหม่ (การขึ้นต่อกันแบบวนซ้ำของระยะ)
ก่อนหน้านี้เราได้พูดถึงว่าเฟสของ Compose จะเรียกใช้ตามลําดับเดิมเสมอ และไม่มีวิธีย้อนกลับขณะที่อยู่ในเฟรมเดียวกัน แต่ก็ไม่ได้ห้ามไม่ให้แอปเข้าสู่ลูปการคอมโพสเฟรมต่างๆ ลองดูตัวอย่างนี้
Box { var imageHeightPx by remember { mutableStateOf(0) } Image( painter = painterResource(R.drawable.rectangle), contentDescription = "I'm above the text", modifier = Modifier .fillMaxWidth() .onSizeChanged { size -> // Don't do this imageHeightPx = size.height } ) Text( text = "I'm below the image", modifier = Modifier.padding( top = with(LocalDensity.current) { imageHeightPx.toDp() } ) ) }
ที่นี่เราใช้คอลัมน์แนวตั้ง (ไม่ดี) โดยมีรูปภาพอยู่ด้านบน แล้วตามด้วยข้อความด้านล่าง เราใช้ Modifier.onSizeChanged()
เพื่อดูขนาดที่ปรับขนาดแล้วของรูปภาพ แล้วใช้ Modifier.padding()
ในข้อความเพื่อเลื่อนข้อความลง Conversion ที่ผิดปกติจาก Px
กลับไปเป็น Dp
บ่งชี้ว่าโค้ดมีปัญหา
ปัญหาของตัวอย่างนี้คือเราไม่ได้มาถึงเลย์เอาต์ "สุดท้าย" ภายในเฟรมเดียว โค้ดนี้ใช้เฟรมหลายเฟรม ซึ่งทํางานที่ไม่จําเป็นและส่งผลให้ UI กระโดดไปมาบนหน้าจอสําหรับผู้ใช้
มาดูแต่ละเฟรมเพื่อดูสิ่งที่เกิดขึ้นกัน
ในเฟรมแรก imageHeightPx
จะมีค่าเป็น 0 และข้อความจะแสดงพร้อมกับ Modifier.padding(top = 0)
จากนั้นจะเข้าสู่ระยะเลย์เอาต์ และระบบจะเรียกใช้การเรียกกลับสําหรับตัวแก้ไข onSizeChanged
ในกรณีนี้ imageHeightPx
จะอัปเดตเป็นความสูงจริงของรูปภาพ
กำหนดเวลาการจัดองค์ประกอบใหม่สำหรับเฟรมถัดไป ในขั้นตอนการวาด ระบบจะแสดงผลข้อความโดยมีการเว้นวรรค 0 เนื่องจากค่าที่เปลี่ยนแปลงยังไม่แสดง
จากนั้นคอมโพสิชันจะเริ่มเฟรมที่ 2 ตามกำหนดการโดยการเปลี่ยนแปลงค่าของ imageHeightPx
ระบบจะอ่านสถานะในบล็อกเนื้อหาของ Box และเรียกใช้สถานะดังกล่าวในเฟสการคอมโพสิชัน ในครั้งนี้ ข้อความจะมีระยะห่างจากขอบที่ตรงกับความสูงของรูปภาพ ในเฟสเลย์เอาต์ โค้ดจะตั้งค่า imageHeightPx
อีกครั้ง แต่ไม่มีการกำหนดเวลาการจัดองค์ประกอบใหม่เนื่องจากค่ายังคงเหมือนเดิม
สุดท้าย เราได้รับระยะห่างจากขอบที่ต้องการในข้อความ แต่การใช้เฟรมเพิ่มเพื่อส่งค่าระยะห่างจากขอบกลับไปยังเฟสอื่นนั้นไม่เหมาะอย่างยิ่งและจะส่งผลให้เฟรมมีเนื้อหาที่ซ้อนทับกัน
ตัวอย่างนี้อาจดูจงใจ แต่โปรดระวังรูปแบบทั่วไปนี้
Modifier.onSizeChanged()
,onGloballyPositioned()
หรือการดำเนินการอื่นๆ ในเลย์เอาต์- อัปเดตสถานะบางส่วน
- ใช้สถานะนั้นเป็นอินพุตสำหรับตัวแก้ไขเลย์เอาต์ (
padding()
,height()
หรือที่คล้ายกัน) - อาจซ้ำ
การแก้ไขสำหรับตัวอย่างข้างต้นคือการใช้องค์ประกอบพื้นฐานของเลย์เอาต์ที่เหมาะสม ตัวอย่างด้านบนสามารถติดตั้งใช้งานด้วย Column()
ง่ายๆ แต่คุณอาจมีตัวอย่างที่ซับซ้อนกว่าซึ่งต้องใช้สิ่งที่กําหนดเอง ซึ่งจะต้องเขียนเลย์เอาต์ที่กําหนดเอง ดูข้อมูลเพิ่มเติมได้ที่คู่มือเลย์เอาต์ที่กำหนดเอง
หลักการทั่วไปในที่นี้คือต้องมีแหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียวสําหรับองค์ประกอบ UI หลายรายการที่ควรวัดและวางไว้โดยคำนึงถึงองค์ประกอบอื่นๆ การใช้องค์ประกอบพื้นฐานของเลย์เอาต์ที่เหมาะสมหรือการสร้างเลย์เอาต์ที่กำหนดเองหมายความว่าองค์ประกอบหลักที่แชร์แบบจํากัดจะทำหน้าที่เป็นแหล่งข้อมูลที่เชื่อถือได้ซึ่งสามารถประสานความสัมพันธ์ระหว่างองค์ประกอบหลายรายการ การใช้สถานะแบบไดนามิกเป็นการละเมิดหลักการนี้
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- State และ Jetpack Compose
- รายการและตารางกริด
- Kotlin สำหรับ Jetpack Compose