这一节了解一下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.launch
class 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.layoutInfo
val visibleItemsInfo = layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isEmpty()) return@derivedStateOf false
val 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对象。