แอปจำนวนมากจำเป็นต้องแสดงคอลเล็กชันรายการ เอกสารนี้จะอธิบายวิธีที่คุณ ก็ทำได้ใน Jetpack Compose
หากคุณทราบว่ากรณีการใช้งานของคุณไม่จำเป็นต้องมีการเลื่อนหน้าจอใดๆ คุณอาจต้องการ
ใช้ Column
หรือ Row
แบบง่าย (ขึ้นอยู่กับทิศทาง) และแสดงเนื้อหาของแต่ละรายการตาม
ทำซ้ำผ่านรายการในลักษณะต่อไปนี้
@Composable fun MessageList(messages: List<Message>) { Column { messages.forEach { message -> MessageRow(message) } } }
เราสามารถทำให้ Column
เลื่อนได้โดยใช้ตัวปรับแต่ง verticalScroll()
รายการแบบ Lazy Loading
หากต้องการแสดงรายการจำนวนมาก (หรือรายการที่ไม่ทราบความยาว)
การใช้เลย์เอาต์ เช่น Column
อาจก่อให้เกิดปัญหาด้านประสิทธิภาพ เนื่องจากรายการทั้งหมดจะได้รับการเขียนและจัดวางในตำแหน่งว่าจะแสดงหรือไม่
Compose มีชุดคอมโพเนนต์ที่ใช้เขียนและจัดเลย์เอาต์เฉพาะรายการที่
มองเห็นได้ในวิวพอร์ตของคอมโพเนนต์ องค์ประกอบเหล่านี้ประกอบด้วย
LazyColumn
และ
LazyRow
ความแตกต่างระหว่าง
LazyColumn
และ
LazyRow
คือการจัดวางสินค้าและการเลื่อน LazyColumn
จะสร้างรายการแบบเลื่อนในแนวตั้ง และ LazyRow
จะสร้างรายการแนวนอน
รายการแบบเลื่อน
คอมโพเนนต์แบบ Lazy จะแตกต่างจากเลย์เอาต์ส่วนใหญ่ใน Compose แทนที่จะเป็น
การยอมรับพารามิเตอร์บล็อกเนื้อหา @Composable
ซึ่งทำให้แอป
ปล่อย Composable ส่วนคอมโพเนนต์ Lazy จะมีบล็อก LazyListScope.()
ช่วงเวลานี้
LazyListScope
บล็อกมี DSL ซึ่งช่วยให้แอปอธิบายเนื้อหาในรายการได้
จากนั้นคอมโพเนนต์แบบ Lazy Loading จะเพิ่มเนื้อหาของแต่ละรายการเป็น
ที่จำเป็นสำหรับเลย์เอาต์และตำแหน่งการเลื่อน
DSL LazyListScope
DSL ของ LazyListScope
มีฟังก์ชันจำนวนมากสำหรับการอธิบายรายการ
ในเลย์เอาต์ เบื้องต้น
item()
เพิ่มรายการเดียว และ
items(Int)
เพิ่มหลายรายการ
LazyColumn { // Add a single item item { Text(text = "First item") } // Add 5 items items(5) { index -> Text(text = "Item: $index") } // Add another single item item { Text(text = "Last item") } }
นอกจากนี้ยังมีฟังก์ชันส่วนขยายอีกมากมายที่จะช่วยให้คุณเพิ่ม
คอลเล็กชันของรายการ เช่น List
ส่วนขยายเหล่านี้ช่วยให้เรา
ย้ายข้อมูลตัวอย่าง Column
จากด้านบนดังนี้
/** * import androidx.compose.foundation.lazy.items */ LazyColumn { items(messages) { message -> MessageRow(message) } }
นอกจากนี้ยังมี
items()
ฟังก์ชันส่วนขยายที่ชื่อว่า
itemsIndexed()
,
ซึ่งจะมีดัชนี โปรดดู
LazyListScope
ข้อมูลอ้างอิงสำหรับรายละเอียดเพิ่มเติม
ตารางกริดแบบ Lazy Loading
LazyVerticalGrid
และ
LazyHorizontalGrid
Composables รองรับการแสดงรายการในตารางกริด ตารางกริดแนวตั้งแบบ Lazy
จะแสดงรายการต่างๆ ในภาชนะที่เลื่อนได้ในแนวตั้ง
หลายคอลัมน์ ในขณะที่ตารางกริดแนวนอนแบบ Lazy ก็จะมีลักษณะการทำงานเหมือนกัน
บนแกนแนวนอน
ตารางกริดมีความสามารถ API ที่มีประสิทธิภาพเช่นเดียวกับรายการและยังใช้
DSL ที่คล้ายกันมาก -
LazyGridScope.()
ในการอธิบายเนื้อหา
พารามิเตอร์ columns
ใน
LazyVerticalGrid
และพารามิเตอร์ rows
รายการใน
LazyHorizontalGrid
ควบคุมวิธีสร้างเซลล์ให้เป็นคอลัมน์หรือแถว ดังต่อไปนี้
ตัวอย่าง แสดงรายการในตารางกริด โดยใช้
GridCells.Adaptive
เพื่อตั้งค่าแต่ละคอลัมน์ให้กว้างอย่างน้อย 128.dp
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 128.dp) ) { items(photos) { photo -> PhotoItem(photo) } }
LazyVerticalGrid
ให้คุณระบุความกว้างของรายการ จากนั้นตารางกริดจะ
ให้พอดีกับคอลัมน์ต่างๆ มากที่สุด กระจายความกว้างที่เหลือเท่าๆ กัน
ระหว่างคอลัมน์ต่างๆ หลังจากคำนวณจำนวนคอลัมน์แล้ว
วิธีปรับขนาดแบบปรับเปลี่ยนได้นี้มีประโยชน์มากสำหรับการแสดงชุดสินค้า
สำหรับหน้าจอขนาดต่างๆ
หากคุณทราบจำนวนคอลัมน์ที่จะใช้จริงๆ คุณสามารถระบุ
อินสแตนซ์ของ
GridCells.Fixed
ที่มีจำนวนคอลัมน์ที่จำเป็น
หากการออกแบบของคุณกำหนดให้สินค้าบางรายการมีขนาดไม่เป็นไปตามมาตรฐาน
คุณจะใช้การรองรับตารางกริดเพื่อระบุช่วงคอลัมน์ที่กําหนดเองสําหรับรายการต่างๆ ได้
ระบุช่วงคอลัมน์ด้วยพารามิเตอร์ span
ของ
LazyGridScope DSL
item
และ items
maxLineSpan
ค่าใดค่าหนึ่งของขอบเขตสแปน ซึ่งจะเป็นประโยชน์อย่างยิ่งเมื่อคุณใช้
การปรับขนาดอัตโนมัติ เนื่องจากจำนวนคอลัมน์ไม่คงที่
ตัวอย่างนี้แสดงวิธีระบุช่วงแถวแบบเต็ม
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 30.dp) ) { item(span = { // LazyGridItemSpanScope: // maxLineSpan GridItemSpan(maxLineSpan) }) { CategoryCard("Fruits") } // ... }
ตารางกริดแบบ Lazy Loading
LazyVerticalStaggeredGrid
และ
LazyHorizontalStaggeredGrid
เป็น Composable ที่ช่วยให้คุณสร้างตารางกริดที่จัดเรียงแบบ Lazy Loading และรายการได้
ตารางกริดแบบ Lazy Loading ในแนวตั้งที่แสดงสินค้าแบบเลื่อนได้ในแนวตั้ง
ที่ครอบคลุมหลายคอลัมน์และทำให้แต่ละรายการสามารถ
ความสูงที่ต่างกัน ตารางกริดแนวนอนแบบ Lazy Loading จะมีลักษณะแบบเดียวกันบน
แกนแนวนอนที่มีรายการความกว้างต่างกัน
ตัวอย่างต่อไปนี้เป็นตัวอย่างพื้นฐานของการใช้ LazyVerticalStaggeredGrid
ที่มีความกว้าง 200.dp
ต่อรายการ:
LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Adaptive(200.dp), verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), content = { items(randomSizedPhotos) { photo -> AsyncImage( model = photo, contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier.fillMaxWidth().wrapContentHeight() ) } }, modifier = Modifier.fillMaxSize() )
หากต้องการกำหนดจำนวนคอลัมน์คงที่ คุณสามารถใช้
StaggeredGridCells.Fixed(columns)
จากราคาเต็ม StaggeredGridCells.Adaptive
ซึ่งจะหารความกว้างที่มีด้วยจำนวนคอลัมน์ (หรือแถวสำหรับ
ตารางแนวนอน) และให้แต่ละรายการใช้ความกว้างนั้น (หรือความสูงสำหรับ
ตารางกริดแนวนอน):
LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Fixed(3), verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), content = { items(randomSizedPhotos) { photo -> AsyncImage( model = photo, contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier.fillMaxWidth().wrapContentHeight() ) } }, modifier = Modifier.fillMaxSize() )
![ตารางกริดที่จัดเรียงแบบ Lazy Loading ของรูปภาพใน Compose](https://developer.android.com/static/develop/ui/compose/images/lists/staggered_grid_fixed.png?hl=th)
ระยะห่างจากขอบของเนื้อหา
บางครั้ง คุณจะต้องเพิ่มระยะห่างจากขอบรอบขอบของเนื้อหา คนขี้เกียจ
จะช่วยให้คุณสามารถส่ง
PaddingValues
ลงในพารามิเตอร์ contentPadding
เพื่อรองรับ
LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), ) { // ... }
ในตัวอย่างนี้ เราเพิ่มระยะห่างจากขอบแนวนอน 16.dp
กับขอบแนวนอน (ด้านซ้ายและ
ขวา) แล้วกด 8.dp
ไปด้านบนและด้านล่างของเนื้อหา
โปรดทราบว่าระยะห่างจากขอบนี้จะมีผลกับเนื้อหา ไม่ใช่กับ
LazyColumn
เอง ในตัวอย่างด้านบน รายการแรกจะเพิ่ม 8.dp
ระยะห่างจากขอบด้านบน รายการสุดท้ายจะเพิ่ม 8.dp
ไว้ด้านล่าง และรายการทั้งหมด
จะมีระยะห่างจากขอบ 16.dp
ทางด้านซ้ายและด้านขวา
ระยะห่างของเนื้อหา
หากต้องการเพิ่มการเว้นระยะห่างระหว่างรายการ คุณสามารถใช้
Arrangement.spacedBy()
ตัวอย่างด้านล่างเพิ่มพื้นที่ระหว่างแต่ละรายการ 4.dp
LazyColumn( verticalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
ในทำนองเดียวกันสำหรับ LazyRow
LazyRow( horizontalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
แต่ตารางกริดยอมรับทั้งแนวตั้งและแนวนอน ดังนี้
LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { items(photos) { item -> PhotoItem(item) } }
คีย์รายการ
โดยค่าเริ่มต้น สถานะของแต่ละรายการจะถูกเชื่อมโยงกับตำแหน่งของรายการใน
รายการหรือตาราง แต่กรณีนี้อาจทําให้เกิดปัญหาหากชุดข้อมูลมีการเปลี่ยนแปลง เนื่องจาก
เปลี่ยนตำแหน่งทันทีที่จะสูญเสียสถานะที่จดจำไว้ ถ้าคุณจินตนาการ
สถานการณ์จำลอง LazyRow
ใน LazyColumn
ถ้าแถวเปลี่ยนตำแหน่งรายการ
ซึ่งจะทำให้ผู้ใช้สูญเสียตำแหน่งการเลื่อนภายในแถว
ในการแก้ปัญหานี้ คุณอาจเตรียมคีย์ที่เสถียรและไม่ซ้ำกันสำหรับแต่ละรายการ โดยให้
การบล็อกพารามิเตอร์ key
การระบุคีย์ที่เสถียรจะทำให้สถานะของรายการสามารถ
สอดคล้องกันในการเปลี่ยนแปลงชุดข้อมูลทั้งหมด
LazyColumn { items( items = messages, key = { message -> // Return a stable + unique key for the item message.id } ) { message -> MessageRow(message) } }
การระบุคีย์จะช่วยให้คุณ Compose ในการจัดการการจัดเรียงใหม่อย่างถูกต้อง ตัวอย่างเช่น หากรายการมีสถานะ "จำแล้ว" การตั้งค่าคีย์จะอนุญาตให้ เขียนเพื่อย้ายสถานะนี้ไปพร้อมกับรายการ เมื่อตำแหน่งเปลี่ยนไป
LazyColumn { items(books, key = { it.id }) { val rememberedValue = remember { Random.nextInt() } } }
อย่างไรก็ตาม ประเภทที่คุณใช้เป็นคีย์รายการได้มีข้อจำกัด 1 ข้อ
ประเภทคีย์ต้องได้รับการสนับสนุนโดย
Bundle
ซึ่งเป็นกลไกของ Android ที่ใช้เก็บ
สถานะเมื่อมีการสร้างกิจกรรมอีกครั้ง Bundle
รองรับประเภท เช่น ประเภทพื้นฐาน
enum หรือ Parcelables
LazyColumn { items(books, key = { // primitives, enums, Parcelable, etc. }) { // ... } }
คีย์ต้องได้รับการรองรับโดย Bundle
เพื่อให้ rememberSaveable
ที่อยู่ข้างใน
สามารถกู้คืนรายการ Composable ได้เมื่อมีการสร้างกิจกรรมใหม่ หรือ
เมื่อคุณเลื่อนออกไปจาก
รายการนี้และเลื่อนกลับ
LazyColumn { items(books, key = { it.id }) { val rememberedValue = rememberSaveable { Random.nextInt() } } }
ภาพเคลื่อนไหวของรายการ
หากใช้วิดเจ็ต RecyclerView คุณจะทราบได้ว่าวิดเจ็ตทำให้รายการเคลื่อนไหว
โดยอัตโนมัติ
เลย์เอาต์แบบ Lazy Loading มีฟังก์ชันแบบเดียวกันสําหรับการจัดเรียงสินค้าใหม่
API นั้นใช้งานง่าย เพียงคุณตั้งค่า
animateItemPlacement
ตัวแก้ไขเนื้อหารายการ
LazyColumn { items(books, key = { it.id }) { Row(Modifier.animateItemPlacement()) { // ... } } }
คุณยังสามารถระบุข้อกำหนดสำหรับภาพเคลื่อนไหวที่กำหนดเองได้ หากต้องการ
LazyColumn { items(books, key = { it.id }) { Row( Modifier.animateItemPlacement( tween(durationMillis = 250) ) ) { // ... } } }
ตรวจสอบว่าคุณได้ระบุคีย์สำหรับรายการแล้วเพื่อให้สามารถหาคีย์ใหม่ ตำแหน่งสำหรับองค์ประกอบที่ย้าย
นอกจากการเรียงลำดับใหม่ ปัจจุบันภาพเคลื่อนไหวของรายการสำหรับการเพิ่มหรือนำออกยัง กำลังพัฒนา คุณสามารถติดตามความคืบหน้าได้ใน ปัญหา 150812265
ส่วนหัวแบบติดหนึบ (ทดลอง)
รูปแบบ "ส่วนหัวแบบติดหนึบ" มีประโยชน์เมื่อแสดงรายการข้อมูลที่จัดกลุ่ม ด้านล่างนี้เป็นตัวอย่าง "รายการรายชื่อติดต่อ" ซึ่งจัดกลุ่มตามรายชื่อติดต่อ ชื่อย่อ:
หากต้องการได้ส่วนหัวแบบติดหนึบด้วย LazyColumn
คุณสามารถใช้
stickyHeader()
โดยให้เนื้อหาส่วนหัวดังนี้
@OptIn(ExperimentalFoundationApi::class) @Composable fun ListWithHeader(items: List<Item>) { LazyColumn { stickyHeader { Header() } items(items) { item -> ItemRow(item) } } }
ถ้าต้องการสร้างรายการที่มีส่วนหัวหลายรายการ เช่น ตัวอย่าง "รายชื่อติดต่อ" ด้านบน สิ่งที่คุณทำได้มีดังนี้
// This ideally would be done in the ViewModel val grouped = contacts.groupBy { it.firstName[0] } @OptIn(ExperimentalFoundationApi::class) @Composable fun ContactsList(grouped: Map<Char, List<Contact>>) { LazyColumn { grouped.forEach { (initial, contactsForInitial) -> stickyHeader { CharacterHeader(initial) } items(contactsForInitial) { contact -> ContactListItem(contact) } } } }
แสดงความรู้สึกต่อตำแหน่งการเลื่อน
แอปจำนวนมากต้องโต้ตอบและฟังการเปลี่ยนแปลงตำแหน่งการเลื่อนและเลย์เอาต์รายการ
คอมโพเนนต์แบบ Lazy จะรองรับกรณีการใช้งานนี้ด้วยการรอก
LazyListState
:
@Composable fun MessageList(messages: List<Message>) { // Remember our own LazyListState val listState = rememberLazyListState() // Provide it to LazyColumn LazyColumn(state = listState) { // ... } }
สำหรับกรณีการใช้งานทั่วไป โดยทั่วไปแอปจำเป็นต้องรู้เฉพาะข้อมูลเกี่ยวกับ
รายการแรกที่ปรากฏ สำหรับกรณีนี้
LazyListState
จะมี
firstVisibleItemIndex
และ
firstVisibleItemScrollOffset
พร็อพเพอร์ตี้
หากเราใช้ตัวอย่างการแสดงและการซ่อนปุ่ม โดยพิจารณาว่าผู้ใช้เลื่อนผ่านรายการแรกหรือไม่
@OptIn(ExperimentalAnimationApi::class) @Composable fun MessageList(messages: List<Message>) { Box { val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } // Show the button if the first visible item is past // the first item. We use a remembered derived state to // minimize unnecessary compositions val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() } } }
การอ่านสถานะในการเขียนโดยตรงจะมีประโยชน์เมื่อคุณต้องการอัปเดต
UI อื่นๆ ที่เป็น Composable อื่น แต่ก็มีบางสถานการณ์ที่ไม่จำเป็นต้อง
ในการเรียบเรียงเดียวกัน ตัวอย่างทั่วไปของเหตุการณ์นี้คือการส่ง
Analytics เมื่อผู้ใช้เลื่อนผ่านจุดหนึ่งๆ วิธีจัดการกับเรื่องนี้
เราสามารถใช้
snapshotFlow()
:
val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } LaunchedEffect(listState) { snapshotFlow { listState.firstVisibleItemIndex } .map { index -> index > 0 } .distinctUntilChanged() .filter { it } .collect { MyAnalyticsService.sendScrolledPastFirstItemEvent() } }
LazyListState
ยังให้ข้อมูลเกี่ยวกับรายการทั้งหมดที่กำลัง
และขอบบนหน้าจอ ผ่านทางแท็ก
layoutInfo
โปรดดู
LazyListLayoutInfo
เพื่อดูข้อมูลเพิ่มเติม
การควบคุมตำแหน่งการเลื่อน
นอกจากการตอบสนองต่อตำแหน่งการเลื่อนแล้ว ยังเป็นประโยชน์สำหรับแอปที่สามารถ
ควบคุมตำแหน่งการเลื่อนด้วย
LazyListState
รองรับฟีเจอร์นี้ผ่านทาง scrollToItem()
ซึ่งจะสแนป
ตำแหน่งการเลื่อน และ animateScrollToItem()
ซึ่งการเลื่อนโดยใช้ภาพเคลื่อนไหว (หรือที่เรียกว่าการเลื่อนอย่างราบรื่น)
@Composable fun MessageList(messages: List<Message>) { val listState = rememberLazyListState() // Remember a CoroutineScope to be able to launch val coroutineScope = rememberCoroutineScope() LazyColumn(state = listState) { // ... } ScrollToTopButton( onClick = { coroutineScope.launch { // Animate scroll to the first item listState.animateScrollToItem(index = 0) } } ) }
ชุดข้อมูลขนาดใหญ่ (การแบ่งหน้า)
ไลบรารีการแบ่งหน้าช่วยให้แอปทำสิ่งต่อไปนี้ได้
รองรับรายการจำนวนมาก การโหลดและการแสดงรายการส่วนเล็กๆ เป็น
ตามความจำเป็น หน้า 3.0 ขึ้นไปจะให้การสนับสนุน Compose ผ่าน
คลัง androidx.paging:paging-compose
หากต้องการแสดงรายการเนื้อหาที่แบ่งหน้า เราสามารถใช้
collectAsLazyPagingItems()
ของส่วนขยาย แล้วส่งผ่าน URL ที่แสดงผล
LazyPagingItems
ถึง items()
ใน LazyColumn
เช่นเดียวกับการสนับสนุนการแบ่งหน้าในมุมมอง คุณสามารถ
แสดงตัวยึดตำแหน่งขณะโหลดข้อมูลโดยการตรวจสอบว่า item
เป็น null
หรือไม่ ดังนี้
@Composable fun MessageList(pager: Pager<Int, Message>) { val lazyPagingItems = pager.flow.collectAsLazyPagingItems() LazyColumn { items( lazyPagingItems.itemCount, key = lazyPagingItems.itemKey { it.id } ) { index -> val message = lazyPagingItems[index] if (message != null) { MessageRow(message) } else { MessagePlaceholder() } } } }
เคล็ดลับในการใช้เลย์เอาต์แบบ Lazy Loading
มีเคล็ดลับ 2-3 ข้อที่คุณนำมาพิจารณาเพื่อให้แน่ใจว่าเลย์เอาต์แบบ Lazy Loading จะทำงานได้ตามที่ต้องการ
หลีกเลี่ยงการใช้รายการขนาด 0 พิกเซล
ซึ่งอาจเกิดขึ้นได้ในบางสถานการณ์ เช่น คุณคาดว่าไม่พร้อมกัน ดึงข้อมูลบางอย่าง เช่น รูปภาพ เพื่อเติมให้กับรายการของคุณในขั้นตอนต่อไป ซึ่งจะทำให้เลย์เอาต์แบบ Lazy จัดองค์ประกอบทั้งหมดใน เนื่องจากความสูงเท่ากับ 0 พิกเซล และพอดีกับขนาดทั้งหมด วิวพอร์ต เมื่อรายการโหลดและขยายความสูงของรายการแล้ว เลย์เอาต์แบบ Lazy จะลบรายการอื่นๆ ทั้งหมดที่ไม่ได้เขียนขึ้นโดยไม่จำเป็น ครั้งแรกเพราะขนาดพอดีกับวิวพอร์ตจริงๆ เพื่อหลีกเลี่ยงปัญหานี้ คุณควรตั้งค่าขนาดเริ่มต้นให้กับสินค้าเพื่อให้เลย์เอาต์แบบ Lazy Loading ได้ การคำนวณที่ถูกต้องของจำนวนรายการที่พอดีกับวิวพอร์ต
@Composable fun Item(imageUrl: String) { AsyncImage( model = rememberAsyncImagePainter(model = imageUrl), modifier = Modifier.size(30.dp), contentDescription = null // ... ) }
เมื่อทราบขนาดสินค้าโดยประมาณหลังจากข้อมูล โหลดแบบอะซิงโครนัส แนวทางปฏิบัติที่ดีคือตรวจสอบว่าขนาดของสินค้ายังคงเดิม ก่อนและหลังการโหลด เช่น เพิ่มตัวยึดตำแหน่งบางอย่าง ซึ่งจะช่วยให้การเลื่อนตำแหน่งได้อย่างถูกต้อง
หลีกเลี่ยงการซ้อนคอมโพเนนต์ซึ่งเลื่อนได้ในทิศทางเดียวกัน
การตั้งค่านี้จะมีผลกับกรณีที่การซ้อนรายการย่อยที่เลื่อนได้โดยไม่มีการกำหนดล่วงหน้า
ขนาดภายในระดับบนสุดที่เลื่อนได้ในทิศทางเดียวกัน ตัวอย่างเช่น การพยายาม
ฝังลูก LazyColumn
ที่ไม่มีความสูงคงที่ภายในอุปกรณ์แบบเลื่อนได้ในแนวตั้ง
ผู้ปกครอง Column
:
// throws IllegalStateException Column( modifier = Modifier.verticalScroll(state) ) { LazyColumn { // ... } }
โดยจะได้ผลลัพธ์เดียวกันด้วยการรวม Composable ทั้งหมดแทน
ภายใน LazyColumn
หลัก 1 ราย และใช้ DSL ของตนเองเพื่อส่งต่อ
เนื้อหา ซึ่งจะทำให้ปล่อยรายการเดี่ยวๆ และรายการหลายรายการได้
ทั้งหมดในที่เดียว
LazyColumn { item { Header() } items(data) { item -> PhotoItem(item) } item { Footer() } }
โปรดทราบว่าในกรณีที่คุณซ้อนเลย์เอาต์ทิศทางต่างๆ
ตัวอย่างเช่น อนุญาตรายการหลัก Row
แบบเลื่อนได้ และ LazyColumn
ย่อย
Row( modifier = Modifier.horizontalScroll(scrollState) ) { LazyColumn { // ... } }
เช่นเดียวกับกรณีที่คุณยังใช้เค้าโครงทิศทางเดิมและได้ตั้งค่า ขนาดคงที่สำหรับหน่วยย่อยที่ซ้อนกันอยู่:
Column( modifier = Modifier.verticalScroll(scrollState) ) { LazyColumn( modifier = Modifier.height(200.dp) ) { // ... } }
ระวังการใส่องค์ประกอบหลายรายการไว้ในรายการเดียว
ในตัวอย่างนี้ รายการที่ 2 lambda จะแสดง 2 รายการใน 1 บล็อก
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Item(2) } item { Item(3) } // ... }
เลย์เอาต์แบบ Lazy Loading จะจัดการสิ่งนี้ตามที่คาดไว้ โดยจัดเลย์เอาต์องค์ประกอบ 1 รายการ ตามหลังอีกชิ้นหนึ่ง ราวกับว่าเป็นคนละรายการกัน อย่างไรก็ตาม มีคำแนะนำ ของปัญหาในการทำเช่นนั้น
เมื่อมีการปล่อยองค์ประกอบหลายองค์ประกอบออกมาเป็นส่วนหนึ่งของรายการเดียว องค์ประกอบเหล่านั้นจะมีการจัดการในฐานะ
เอนทิตีเดียว ซึ่งหมายความว่าจะแต่งเพลงเหล่านั้นแยกกันไม่ได้อีก หากมี
จะมองเห็นได้บนหน้าจอ จากนั้นองค์ประกอบทั้งหมดที่สอดคล้องกับ
รายการจะต้องได้รับการแต่งขึ้นและวัดค่า ซึ่งอาจส่งผลเสียต่อประสิทธิภาพหากใช้
มากเกินไป ในกรณีสุดโต่งคือการใส่องค์ประกอบทั้งหมดไว้ในรายการเดียว
ทำให้ไม่สามารถใช้เลย์เอาต์แบบ Lazy Loading ได้ นอกเหนือจากศักยภาพ
ปัญหาด้านประสิทธิภาพ การเพิ่มองค์ประกอบอื่นๆ ในรายการเดียวก็จะเป็นอุปสรรคเช่นกัน
กับ scrollToItem()
และ animateScrollToItem()
อย่างไรก็ตาม มีกรณีการใช้งานที่ถูกต้องสำหรับการวางองค์ประกอบหลายรายการในรายการเดียว เช่น มีเส้นแบ่งอยู่ภายในรายการ คุณไม่ต้องการให้เส้นแบ่งเปลี่ยนการเลื่อน เนื่องจากไม่ควรถือเป็นองค์ประกอบอิสระ นอกจากนี้ ประสิทธิภาพ จะไม่ได้รับผลกระทบเพราะเส้นแบ่งมีขนาดเล็ก ตัวแบ่งอาจต้อง แสดงเมื่อรายการก่อนที่จะเห็น เพื่อให้เป็นส่วนหนึ่งของ รายการ:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Divider() } item { Item(2) } // ... }
ลองใช้การจัดเรียงที่กำหนดเอง
โดยปกติรายการแบบ Lazy Loading ปกติแล้วจะมีหลายรายการ และมีขนาดมากกว่า คอนเทนเนอร์แบบเลื่อนได้ อย่างไรก็ตาม เมื่อในรายการมีการป้อนข้อมูล 2-3 รายการ อาจมีข้อกำหนดที่เฉพาะเจาะจงมากขึ้น เกี่ยวกับวิธีวางตำแหน่งโฆษณา ในวิวพอร์ต
เพื่อให้บรรลุเป้าหมายนี้ คุณสามารถใช้ประเภทธุรกิจที่กำหนดเอง
Arrangement
และส่งไปยัง LazyColumn
ในตัวอย่างต่อไปนี้ TopWithFooter
ต้องใช้เมธอด arrange
เท่านั้น ประการแรก โมเดลจะวางตำแหน่ง
รายการทีละรายการ หากความสูงที่ใช้ทั้งหมดต่ำกว่า
ความสูงของวิวพอร์ตจะวางส่วนท้ายไว้ด้านล่าง
object TopWithFooter : Arrangement.Vertical { override fun Density.arrange( totalSize: Int, sizes: IntArray, outPositions: IntArray ) { var y = 0 sizes.forEachIndexed { index, size -> outPositions[index] = y y += size } if (y < totalSize) { val lastIndex = outPositions.lastIndex outPositions[lastIndex] = totalSize - sizes.last() } } }
โปรดพิจารณาเพิ่ม contentType
ในการ Compose 1.2 เพื่อเพิ่มประสิทธิภาพสูงสุดให้กับ Lazy
ให้พิจารณาเพิ่ม
contentType
ลงในรายการหรือตารางกริด ซึ่งจะช่วยให้คุณระบุประเภทเนื้อหาสำหรับแต่ละเหตุการณ์ได้
รายการเลย์เอาต์ ในกรณีที่คุณกำลังเขียนรายการหรือตารางกริดที่ประกอบด้วย
ของรายการประเภทต่างๆ หลายประเภท
LazyColumn { items(elements, contentType = { it.type }) { // ... } }
เมื่อคุณระบุ
contentType
การเขียนสามารถใช้การเรียบเรียงเพลงซ้ำเพียงอย่างเดียว
ระหว่างรายการที่อยู่ในประเภทเดียวกันได้ เพราะการนำมาใช้ซ้ำจะมีประสิทธิภาพมากกว่าเมื่อคุณ
เขียนรายการที่มีโครงสร้างคล้ายกัน ทำให้ประเภทเนื้อหามั่นใจได้ว่า
การเขียนไม่ได้พยายามเขียนรายการประเภท A ซ้อนทับกับ
รายการอื่นของประเภท B ซึ่งจะช่วยเพิ่มประโยชน์ขององค์ประกอบภาพได้สูงสุด
การนำมาใช้ซ้ำและประสิทธิภาพของเลย์เอาต์แบบ Lazy Loading
การวัดประสิทธิภาพ
คุณจะวัดประสิทธิภาพของเลย์เอาต์แบบ Lazy Loading ได้อย่างน่าเชื่อถือเท่านั้นเมื่อเรียกใช้ใน โหมดเผยแพร่ และเปิดใช้การเพิ่มประสิทธิภาพ R8 ในบิลด์การแก้ไขข้อบกพร่อง เลย์เอาต์แบบ Lazy การเลื่อนอาจปรากฏขึ้นช้าลง สำหรับข้อมูลเพิ่มเติม โปรดอ่าน ประสิทธิภาพของการเขียน
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ย้ายข้อมูล
RecyclerView
ไปยังรายการแบบ Lazy Loading - บันทึกสถานะ UI ใน Compose
- Kotlin สำหรับ Jetpack Compose