Compose笔记(十八)--rememberLazyListState
这一节了解一下Compose中的rememberLazyListState的使用,在 Jetpack Compose 中,rememberLazyListState 是用于管理 LazyColumn 或 LazyRow 滚动状态的核心工具。简单介绍:
API
(1) firstVisibleItemIndex
含义:当前屏幕上第一个可见列表项的索引(从0开始)。
作用:1)监听用户滚动位置(例如滚动到顶部时显示“返回顶部”按钮)。2)结合 firstVisibleItemScrollOffset 实现更精确的滚动控制。
(2) firstVisibleItemScrollOffset
含义:第一个可见列表项在屏幕顶部的偏移量(像素值)。
作用:1)计算列表项的滚动位置(例如滚动到特定偏移量时触发动画)。2)与 firstVisibleItemIndex 配合,实现更精细的滚动状态跟踪。
(3) layoutInfo
含义:包含列表布局的完整信息(如总项数、可见区域尺寸等)。
作用:1)获取列表的总项数(layoutInfo.totalItemsCount)。2)计算列表的可见区域高度(layoutInfo.viewport.size.height),用于分页加载或懒加载。
(4) scrollToItem(index, scrollOffset)
含义:滚动到指定索引的列表项,并可设置偏移量。
作用:1)实现点击跳转(如点击字母索引跳转到对应分组)。2)结合 firstVisibleItemIndex 实现滚动到顶部或底部。
(5) animateScrollToItem(index, scrollOffset)
含义:平滑滚动到指定索引的列表项(带动画效果)。
作用:1)提升用户体验(如从详情页返回列表时平滑滚动到之前的位置)。2)避免突兀的滚动跳转。
(6) isScrollInProgress
含义:布尔值,表示当前是否正在滚动。
作用:1)防止滚动过程中触发其他操作(如避免滚动时加载数据)。2)实现滚动结束后的回调(如滚动停止后加载下一页)。
栗子:
保存滚动位置
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.launchclass ScrollViewModel : ViewModel() {var savedFirstVisibleItemIndex: Int by mutableIntStateOf(0)fun saveScrollState(state: androidx.compose.foundation.lazy.LazyListState) {savedFirstVisibleItemIndex = state.firstVisibleItemIndex}}@Composable
fun ScrollPositionSaveExample() {val viewModel: ScrollViewModel = viewModel()val lazyListState = rememberLazyListState()val scope = rememberCoroutineScope()val itemsList = (1..100).toList()Column {// 手动保存滚动位置的按钮Button(onClick = {viewModel.saveScrollState(lazyListState)},modifier = Modifier.padding(16.dp)) {Text(text = "Save Scroll Position")}// 手动恢复滚动位置的按钮Button(onClick = {scope.launch {lazyListState.scrollToItem(viewModel.savedFirstVisibleItemIndex)}},modifier = Modifier.padding(16.dp)) {Text(text = "Restore Scroll Position", fontSize = 24.sp,color = Color.Blue)}LazyColumn(state = lazyListState) {items(itemsList) { item ->Text(text = "Item $item", modifier = Modifier.padding(16.dp))}}}
}
// 保存滚动
@Composable
fun SaveScrollStateExample() {val lazyListStateInfo = rememberLazyListState()val lazyListState = rememberSaveable(saver = androidx.compose.foundation.lazy.LazyListState.Saver) {lazyListStateInfo}val items = (1..100).toList()LazyColumn(state = lazyListState, modifier = Modifier.fillMaxSize()) {items(items) { item ->Text(text = "Item $item", modifier = Modifier.padding(16.dp))}}
}
滚动到顶部
@Composable
fun ScrollToTopExample() {val listState = rememberLazyListState()val showButton by remember {derivedStateOf { listState.firstVisibleItemIndex > 0 }}Column {LazyColumn(state = listState) {items(100) { index ->Text("Item $index", modifier = Modifier.fillParentMaxWidth())}}AnimatedVisibility(visible = showButton) {Button(onClick = {listState.animateScrollToItem(0) // 平滑滚动到顶部}) {Text("Scroll to Top")}}}
}
分页加载
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember@Composable
fun PaginationExample() {val listState = rememberLazyListState()val isNearBottom by remember {derivedStateOf {val layoutInfo = listState.layoutInfoval visibleItemsInfo = layoutInfo.visibleItemsInfoif (visibleItemsInfo.isEmpty()) return@derivedStateOf falseval lastVisibleItem = visibleItemsInfo.last()lastVisibleItem.index == layoutInfo.totalItemsCount - 1 // 滚动到底部时加载更多}}LaunchedEffect(isNearBottom) {if (isNearBottom) {// 加载下一页数据}}LazyColumn(state = listState) {items(items) { item ->Text(item.text)}}
}
注意
避免不必要的状态更新:LazyListState 是一个可变状态,频繁的状态更新可能会触发不必要的重组。要确保只在必要时更新 LazyListState,例如在用户滚动或者数据变化时。
内存优化:对于超长列表,结合key参数和itemKey参数优化项的复用。
线程安全:LaunchedEffect 中的滚动操作需在主线程执行。
源码:
@Composable
fun rememberLazyListState(initialFirstVisibleItemIndex: Int = 0,initialFirstVisibleItemScrollOffset: Int = 0
): LazyListState {return rememberSaveable(saver = LazyListState.Saver) {LazyListState(initialFirstVisibleItemIndex,initialFirstVisibleItemScrollOffset)}
}
分析:rememberSaveable是一个 Composable 函数,其作用是在组件重新创建时(如屏幕旋转)保存和恢复状态。saver = LazyListState.Saver 这一参数指定了用于保存和恢复 LazyListState的 Saver对象。
@OptIn(ExperimentalFoundationApi::class)
@Stable
class LazyListState constructor(firstVisibleItemIndex: Int = 0,firstVisibleItemScrollOffset: Int = 0
) : ScrollableState {...override suspend fun scroll(scrollPriority: MutatePriority,block: suspend ScrollScope.() -> Unit) = scrollableState.scroll(scrollPriority, block)suspend fun scrollToItem(index: Int,scrollOffset: Int = 0) {// ...}suspend fun animateScrollToItem(index: Int,scrollOffset: Int = 0) {// ...}...
}
分析:
initialFirstVisibleItemIndex:表示初始时第一个可见项的索引,默认值为 0。
initialFirstVisibleItemScrollOffset:表示初始时第一个可见项的滚动偏移量,默认值为0。
scroll方法:实现了 ScrollableState接口的scroll方法,用于处理滚动操作。
scrollToItem方法:将列表滚动到指定索引的项,并且可以指定滚动偏移量。
animateScrollToItem方法:以动画的形式将列表滚动到指定索引的项,同样可以指定滚动偏移量。
val layoutInfo: LazyListLayoutInfo get() = layoutInfoState.value
分析:
layoutInfo属性:对外提供当前列表的布局信息,包含可见项的信息、总项数。
companion object {/*** The default [Saver] implementation for [LazyListState].*/val Saver: Saver<LazyListState, *> = listSaver(save = { listOf(it.firstVisibleItemIndex, it.firstVisibleItemScrollOffset) },restore = {LazyListState(firstVisibleItemIndex = it[0],firstVisibleItemScrollOffset = it[1])})}
分析:
Saver对象用于保存和恢复LazyListState的状态。save方法将firstVisibleItemIndex和firstVisibleItemScrollOffset保存为一个列表,restore方法根据保存的列表恢复LazyListState对象。