Compose แสดงผลเฟรมผ่านระยะต่างๆ ที่แตกต่างกันเช่นเดียวกับชุดเครื่องมือ UI อื่นๆ ส่วนใหญ่ ตัวอย่างเช่น ระบบ Android View มี 3 ระยะหลัก ได้แก่ การวัดค่า เลย์เอาต์ และการวาด Compose มีความคล้ายกันมาก แต่มีเฟสเพิ่มเติมที่สำคัญที่เรียกว่า Composition ในตอนเริ่มต้น
เอกสารประกอบของ Compose อธิบาย Composition ไว้ในการคิดใน Compose และสถานะและ Jetpack Compose
3 ระยะของเฟรม
Compose มี 3 ระยะหลักๆ ดังนี้
- Composition: สิ่งที่จะแสดงใน UI Compose จะเรียกใช้ฟังก์ชันที่ประกอบกันได้และสร้างคำอธิบาย UI ของคุณ
- เลย์เอาต์: ตำแหน่งที่จะวาง UI ระยะนี้ประกอบด้วย 2 ขั้นตอน ได้แก่ การวัดผลและการจัดวาง องค์ประกอบเลย์เอาต์จะวัดและวางตำแหน่งตัวเองและองค์ประกอบย่อยในพิกัด 2 มิติสำหรับแต่ละโหนดในโครงสร้างเลย์เอาต์
- การวาด: วิธีแสดงผล องค์ประกอบ UI จะวาดลงใน Canvas ซึ่งมักจะเป็นหน้าจออุปกรณ์
โดยทั่วไปแล้ว ลำดับของระยะเหล่านี้จะเหมือนกัน ซึ่งช่วยให้ข้อมูลไหลไปในทิศทางเดียวตั้งแต่การจัดองค์ประกอบไปจนถึงเลย์เอาต์และการวาดเพื่อสร้างเฟรม (หรือที่เรียกว่าการไหลของข้อมูลแบบทิศทางเดียว) BoxWithConstraints, LazyColumn และ LazyRow เป็นข้อยกเว้นที่สำคัญ โดยองค์ประกอบขององค์ประกอบย่อยจะขึ้นอยู่กับระยะเลย์เอาต์ขององค์ประกอบหลัก
ในทางแนวคิด แต่ละระยะเหล่านี้จะเกิดขึ้นสำหรับทุกเฟรม อย่างไรก็ตาม เพื่อเพิ่มประสิทธิภาพ Compose จะหลีกเลี่ยงการทำงานซ้ำซึ่งจะคำนวณผลลัพธ์เดียวกันจากอินพุตเดียวกันในทุกระยะเหล่านี้ Compose จะข้ามการเรียกใช้ฟังก์ชันที่ประกอบกันได้หากสามารถนำผลลัพธ์เดิมมาใช้ซ้ำ และ Compose UI จะไม่จัดเลย์เอาต์หรือวาดแผนผังทั้งหมดใหม่หากไม่จำเป็น Compose จะทำงานในปริมาณขั้นต่ำที่จำเป็นต่อการอัปเดต UI เท่านั้น การเพิ่มประสิทธิภาพนี้เป็นไปได้เนื่องจาก Compose ติดตามการอ่านสถานะภายในระยะต่างๆ
ทำความเข้าใจระยะต่างๆ
ส่วนนี้จะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีดำเนินการ 3 ระยะของ Compose สำหรับ Composable
การเรียบเรียง
ในระยะการคอมโพสิชัน รันไทม์ของ Compose จะเรียกใช้ฟังก์ชันที่ประกอบกันได้และแสดงผลโครงสร้างแบบต้นไม้ที่แสดงถึง UI แผนผัง UI นี้ประกอบด้วยโหนดเลย์เอาต์ที่มีข้อมูลทั้งหมดที่จำเป็นสำหรับระยะถัดไป ดังที่แสดงในวิดีโอต่อไปนี้
รูปที่ 2 โครงสร้างที่แสดง UI ของคุณซึ่งสร้างขึ้นในระยะการจัดองค์ประกอบ
ส่วนย่อยของโค้ดและโครงสร้าง UI จะมีลักษณะดังนี้
ในตัวอย่างเหล่านี้ ฟังก์ชันที่ประกอบกันได้แต่ละฟังก์ชันในโค้ดจะแมปกับโหนดเลย์เอาต์เดียวในแผนผัง UI ในตัวอย่างที่ซับซ้อนมากขึ้น Composable อาจมีตรรกะและโฟลว์การควบคุม และสร้างต้นไม้ที่แตกต่างกันตามสถานะที่ต่างกัน
การจัดวาง
ในระยะเลย์เอาต์ Compose จะใช้ต้นไม้ UI ที่สร้างขึ้นในระยะการคอมโพสเป็นอินพุต คอลเล็กชันของโหนดเลย์เอาต์มีข้อมูลทั้งหมดที่จำเป็นในการตัดสินใจเกี่ยวกับขนาดและตำแหน่งของแต่ละโหนดในพื้นที่ 2 มิติ
รูปที่ 4 การวัดและการวางโหนดเลย์เอาต์แต่ละโหนดในแผนผัง UI ระหว่างระยะเลย์เอาต์
ในระหว่างระยะเลย์เอาต์ ระบบจะข้ามโครงสร้างโดยใช้อัลกอริทึม 3 ขั้นตอนต่อไปนี้
- วัดโหนดย่อย: โหนดจะวัดโหนดย่อยหากมี
- กำหนดขนาดของตัวเอง: โหนดจะกำหนดขนาดของตัวเองตามการวัดเหล่านี้
- วางโหนดบุตรหลาน: โหนดบุตรหลานแต่ละโหนดจะวางตามตำแหน่งของโหนดเอง
เมื่อสิ้นสุดระยะนี้ โหนดเลย์เอาต์แต่ละโหนดจะมีสิ่งต่อไปนี้
- ความกว้างและความสูงที่กำหนด
- พิกัด x, y ที่ควรวาด
โปรดนึกถึงแผนผัง UI จากส่วนก่อนหน้า
สำหรับแผนภูมินี้ อัลกอริทึมจะทำงานดังนี้
Rowจะวัดองค์ประกอบย่อยImageและColumn- ระบบจะวัดค่า
Imageเนื่องจากไม่มีองค์ประกอบย่อย จึงกำหนดขนาดของตัวเองและรายงานขนาดกลับไปยังRow - จากนั้นระบบจะวัด
Columnโดยจะวัดองค์ประกอบย่อยของตัวเอง (TextComposable 2 รายการ) ก่อน - ระบบจะวัด
Textแรก เนื่องจากไม่มีองค์ประกอบย่อย จึงกำหนดขนาดของตัวเองและรายงานขนาดกลับไปยังColumn- ระบบจะวัดค่า
Textที่ 2 เนื่องจากไม่มีองค์ประกอบย่อย จึงกำหนดขนาดของตัวเองและรายงานกลับไปยังColumn
- ระบบจะวัดค่า
Columnจะใช้การวัดขนาดขององค์ประกอบย่อยเพื่อกำหนดขนาดของตัวเอง โดยจะใช้ความกว้างสูงสุดขององค์ประกอบย่อยและผลรวมความสูงขององค์ประกอบย่อยColumnจะวางองค์ประกอบย่อยโดยอิงตามตัวมันเอง โดยวางองค์ประกอบย่อยไว้ใต้กันและกันในแนวตั้งRowจะใช้การวัดขนาดขององค์ประกอบย่อยเพื่อกำหนดขนาดของตัวเอง โดยจะใช้ความสูงสูงสุดขององค์ประกอบย่อยและผลรวมของความกว้างขององค์ประกอบย่อย จากนั้นจึงวางองค์ประกอบย่อย
โปรดทราบว่าระบบจะเข้าชมแต่ละโหนดเพียงครั้งเดียว รันไทม์ของ Compose ต้องผ่านแผนผัง UI เพียงครั้งเดียวเพื่อวัดและวางโหนดทั้งหมด ซึ่งจะช่วยปรับปรุงประสิทธิภาพ เมื่อจำนวนโหนดในต้นไม้เพิ่มขึ้น เวลาที่ใช้ในการข้ามผ่านจะเพิ่มขึ้นในลักษณะเชิงเส้น ในทางตรงกันข้าม หากมีการเข้าชมแต่ละโหนดหลายครั้ง เวลาในการส่งผ่านจะเพิ่มขึ้นแบบทวีคูณ
การวาด
ในระยะการวาด ระบบจะข้ามต้นไม้อีกครั้งจากบนลงล่าง และแต่ละโหนดจะวาดตัวเองบนหน้าจอตามลำดับ
รูปที่ 5 ระยะการวาดจะวาดพิกเซลบนหน้าจอ
จากตัวอย่างก่อนหน้านี้ เนื้อหาแบบต้นไม้จะแสดงในลักษณะต่อไปนี้
Rowจะวาดเนื้อหาที่อาจมี เช่น สีพื้นหลังImageวาดตัวเองColumnวาดตัวเองTextตัวแรกและตัวที่ 2 จะวาดตัวเองตามลำดับ
รูปที่ 6 แผนผัง UI และการแสดงผลที่วาดขึ้น
การอ่านสถานะ
เมื่อคุณอ่าน value ของ snapshot state ในระหว่างระยะใดระยะหนึ่งที่ระบุไว้ก่อนหน้านี้ Compose จะติดตามสิ่งที่กำลังทำอยู่โดยอัตโนมัติเมื่ออ่าน value การติดตามนี้ช่วยให้ Compose เรียกใช้ Reader อีกครั้งเมื่อ value ของสถานะมีการเปลี่ยนแปลง และเป็นพื้นฐานของความสามารถในการสังเกตสถานะใน Compose
โดยทั่วไป คุณจะสร้างสถานะโดยใช้ mutableStateOf() แล้วเข้าถึงสถานะนั้นผ่านวิธีใดวิธีหนึ่งจาก 2 วิธี ได้แก่ การเข้าถึงพร็อพเพอร์ตี้ value โดยตรง หรือการใช้ผู้รับมอบสิทธิ์พร็อพเพอร์ตี้ Kotlin อ่านข้อมูลเพิ่มเติมเกี่ยวกับสถานะได้ในสถานะใน Composable สำหรับวัตถุประสงค์ของคู่มือนี้ "การอ่านสถานะ" หมายถึงวิธีการเข้าถึงที่เทียบเท่ากันอย่างใดอย่างหนึ่ง
// 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) )
เบื้องหลังของผู้รับมอบสิทธิ์พร็อพเพอร์ตี้ ฟังก์ชัน "getter" และ "setter" จะใช้เพื่อเข้าถึงและอัปเดต value ของ State ระบบจะเรียกใช้ฟังก์ชัน Getter และ Setter เหล่านี้เมื่อคุณอ้างอิงพร็อพเพอร์ตี้เป็นค่าเท่านั้น และจะไม่เรียกใช้เมื่อสร้างพร็อพเพอร์ตี้ ซึ่งเป็นสาเหตุที่ทำให้ทั้ง 2 วิธีที่อธิบายไว้ก่อนหน้านี้เทียบเท่ากัน
โค้ดแต่ละบล็อกที่เรียกใช้ซ้ำได้เมื่อสถานะการอ่านเปลี่ยนแปลงคือขอบเขตการรีสตาร์ท Compose จะติดตามการเปลี่ยนแปลงสถานะ value และรีสตาร์ทขอบเขตในระยะต่างๆ
การอ่านสถานะแบบแบ่งระยะ
ดังที่กล่าวไว้ก่อนหน้านี้ Compose มี 3 ระยะหลัก และ Compose จะติดตามสถานะที่อ่านภายในแต่ละระยะ ซึ่งจะช่วยให้ Compose แจ้งเตือนเฉพาะเฟสที่ต้องทำงานสำหรับองค์ประกอบที่ได้รับผลกระทบแต่ละรายการของ UI
ส่วนต่อไปนี้จะอธิบายแต่ละระยะและอธิบายสิ่งที่เกิดขึ้นเมื่อมีการอ่านค่าสถานะภายในระยะนั้น
ระยะที่ 1: การจัดองค์ประกอบ
การอ่านสถานะภายในฟังก์ชัน @Composable หรือบล็อก Lambda จะส่งผลต่อการจัดองค์ประกอบและอาจส่งผลต่อเฟสที่ตามมาด้วย เมื่อ value ของสถานะมีการเปลี่ยนแปลง เครื่องมือจัดองค์ประกอบใหม่จะกำหนดเวลาให้เรียกใช้ฟังก์ชันที่ประกอบกันได้ทั้งหมดซึ่งอ่าน value ของสถานะนั้นอีกครั้ง โปรดทราบว่ารันไทม์อาจตัดสินใจข้ามฟังก์ชันที่ประกอบกันได้บางส่วนหรือทั้งหมดหากอินพุตไม่มีการเปลี่ยนแปลง ดูข้อมูลเพิ่มเติมได้ที่การข้ามหากอินพุตไม่มีการเปลี่ยนแปลง
Compose UI จะเรียกใช้ระยะเลย์เอาต์และการวาดโดยขึ้นอยู่กับผลลัพธ์ขององค์ประกอบ โดยอาจข้ามขั้นตอนเหล่านี้หากเนื้อหายังคงเหมือนเดิม รวมถึงขนาดและเลย์เอาต์ไม่มีการเปลี่ยนแปลง
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 { … } และฟังก์ชันที่คล้ายกัน
การอ่านสถานะในแต่ละขั้นตอนเหล่านี้จะส่งผลต่อเลย์เอาต์และอาจส่งผลต่อระยะการวาด เมื่อ value ของสถานะเปลี่ยนแปลง Compose UI จะกำหนดเวลาเฟสเลย์เอาต์ นอกจากนี้ยังเรียกใช้ระยะการวาดหากขนาดหรือตำแหน่งมีการเปลี่ยนแปลง
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 เมื่อ value ของสถานะเปลี่ยนแปลง 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) { // ... } }
โค้ดนี้ใช้งานได้ แต่ส่งผลให้ประสิทธิภาพไม่ดีเท่าที่ควร ตามที่เขียนไว้ โค้ดจะอ่าน value ของสถานะ firstVisibleItemScrollOffset และส่งไปยังฟังก์ชัน Modifier.offset(offset: Dp) เมื่อผู้ใช้เลื่อน value ของ firstVisibleItemScrollOffset จะเปลี่ยนไป ดังที่ได้เรียนรู้ไปแล้ว Compose จะติดตามการอ่านสถานะเพื่อให้สามารถรีสตาร์ท (เรียกใช้ซ้ำ) โค้ดการอ่านได้ ซึ่งในตัวอย่างนี้คือเนื้อหาของ Box
นี่คือตัวอย่างการอ่านสถานะภายในเฟส composition ซึ่งไม่ใช่เรื่องแย่เสมอไป และที่จริงแล้วเป็นพื้นฐานของการจัดองค์ประกอบใหม่ ซึ่งช่วยให้การเปลี่ยนแปลงข้อมูลแสดง UI ใหม่ได้
ประเด็นสำคัญ: ตัวอย่างนี้ไม่เหมาะสมเนื่องจากเหตุการณ์การเลื่อนทุกครั้งส่งผลให้เนื้อหา Composable ทั้งหมดได้รับการประเมิน วัด จัดวาง และวาดใหม่ในที่สุด คุณเรียกใช้ระยะการเขียนในการเลื่อนทุกครั้งแม้ว่าเนื้อหาที่แสดงจะไม่เปลี่ยนแปลง มีเพียงตำแหน่งเท่านั้น คุณสามารถเพิ่มประสิทธิภาพการอ่านสถานะเพื่อทริกเกอร์เฟสเลย์เอาต์อีกครั้งเท่านั้น
ออฟเซ็ตด้วยแลมบ์ดา
ตัวแก้ไขออฟเซ็ตยังมีอีกเวอร์ชันหนึ่ง ดังนี้
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 จะติดตามเมื่อมีการอ่านสถานะ
การเปลี่ยนแปลงนี้หมายความว่าหาก value ของ firstVisibleItemScrollOffset มีการเปลี่ยนแปลง
Compose จะต้องรีสตาร์ทเฉพาะขั้นตอนเลย์เอาต์และการวาดเท่านั้น
แน่นอนว่าการอ่านสถานะในระยะการจัดองค์ประกอบมักเป็นสิ่งจำเป็นอย่างยิ่ง อย่างไรก็ตาม มีบางกรณีที่คุณสามารถลดจำนวนการจัดองค์ประกอบใหม่ได้โดยการกรองการเปลี่ยนแปลงสถานะ ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่derivedStateOf: แปลงออบเจ็กต์สถานะอย่างน้อย 1 รายการเป็นสถานะอื่น
ลูปการจัดองค์ประกอบใหม่ (การขึ้นต่อกันของระยะที่เป็นวงจร)
คู่มือนี้ได้กล่าวถึงก่อนหน้านี้ว่าเฟสของ Compose จะถูกเรียกใช้ในลำดับเดียวกันเสมอ และไม่มีทางที่จะย้อนกลับไปได้ขณะที่อยู่ในเฟรมเดียวกัน อย่างไรก็ตาม วิธีนี้ไม่ได้ห้ามไม่ให้แอปเข้าสู่ลูปการจัดองค์ประกอบในเฟรมที่แตกต่างกัน ลองดูตัวอย่างนี้
Box { var imageHeightPx by remember { mutableIntStateOf(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() กับข้อความเพื่อเลื่อนลง
การแปลงที่ไม่เป็นธรรมชาติจาก Px กลับไปเป็น Dp บ่งชี้อยู่แล้วว่าโค้ดมีปัญหาบางอย่าง
ปัญหาของตัวอย่างนี้คือโค้ดไม่แสดงเลย์เอาต์ "สุดท้าย" ภายในเฟรมเดียว โค้ดอาศัยเฟรมหลายเฟรมที่เกิดขึ้น ซึ่งทำให้เกิดการทำงานที่ไม่จำเป็น และส่งผลให้ UI กระโดดไปมาบนหน้าจอสำหรับผู้ใช้
องค์ประกอบเฟรมแรก
ในระหว่างระยะการจัดองค์ประกอบของเฟรมแรก imageHeightPx จะเป็น 0 ในตอนแรก ด้วยเหตุนี้ โค้ดจึงแสดงข้อความพร้อม Modifier.padding(top = 0)
ระยะเลย์เอาต์ที่ตามมาจะเรียกใช้ Callback ของตัวแก้ไข onSizeChanged ซึ่งจะอัปเดต imageHeightPx เป็นความสูงจริงของรูปภาพ จากนั้น Compose จะกำหนดเวลาการจัดองค์ประกอบใหม่สำหรับเฟรมถัดไป อย่างไรก็ตาม ในระหว่างขั้นตอนการวาดปัจจุบัน ข้อความจะแสดงผลโดยมีระยะห่างจากขอบ 0 เนื่องจากค่า imageHeightPx ที่อัปเดตยังไม่แสดงผล
องค์ประกอบของเฟรมที่ 2
Compose จะเริ่มเฟรมที่ 2 ซึ่งทริกเกอร์โดยการเปลี่ยนแปลงค่าของ imageHeightPx ในระยะการจัดองค์ประกอบของเฟรมนี้ ระบบจะอ่านสถานะภายในบล็อกเนื้อหา Box ตอนนี้ข้อความมีระยะห่างจากขอบที่ตรงกับความสูงของรูปภาพอย่างถูกต้อง ในระหว่างระยะเลย์เอาต์ ระบบจะตั้งค่า imageHeightPx อีกครั้ง แต่จะไม่มีการกำหนดเวลาการจัดองค์ประกอบใหม่เพิ่มเติมเนื่องจากค่ายังคงสอดคล้องกัน
ตัวอย่างนี้อาจดูเหมือนเป็นเรื่องที่แต่งขึ้น แต่โปรดระวังรูปแบบทั่วไปต่อไปนี้
Modifier.onSizeChanged(),onGloballyPositioned()หรือการดำเนินการอื่นๆ เกี่ยวกับเลย์เอาต์- อัปเดตสถานะบางอย่าง
- ใช้สถานะนั้นเป็นอินพุตสำหรับตัวแก้ไขเลย์เอาต์ (
padding(),height()หรือที่คล้ายกัน) - อาจทำซ้ำ
วิธีแก้ไขตัวอย่างก่อนหน้านี้คือการใช้ Layout Primitives ที่เหมาะสม ตัวอย่างก่อนหน้านี้สามารถใช้ Column() ได้ แต่คุณอาจมีตัวอย่างที่ซับซ้อนกว่าซึ่งต้องใช้สิ่งที่กำหนดเอง ซึ่งจะต้องเขียนเลย์เอาต์ที่กำหนดเอง ดูข้อมูลเพิ่มเติมได้ที่คู่มือเลย์เอาต์ที่กำหนดเอง
หลักการทั่วไปในที่นี้คือการมีแหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียว (SSOT) สำหรับองค์ประกอบ UI หลายรายการที่ควรวัดและวางโดยสัมพันธ์กัน การใช้ Layout Primitive ที่เหมาะสมหรือการสร้างเลย์เอาต์ที่กำหนดเองหมายความว่าองค์ประกอบหลักที่ใช้ร่วมกันขั้นต่ำจะทำหน้าที่เป็นแหล่งข้อมูลที่เชื่อถือได้ซึ่งสามารถประสานความสัมพันธ์ระหว่างองค์ประกอบหลายรายการ การใช้สถานะแบบไดนามิกจะขัดต่อหลักการนี้
ดูข้อมูลเพิ่มเติมเกี่ยวกับลูปการจัดองค์ประกอบใหม่และวิธีหลีกเลี่ยงการเขียนไปยังสถานะในเฟสต่างๆ ได้ที่การเขียนย้อนกลับใน Compose
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- สถานะและ Jetpack Compose
- รายการและตารางกริด
- Kotlin สำหรับ Jetpack Compose