当前位置: 首页 > news >正文

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对象。

相关文章:

  • 【第11节 嵌入式软件的组成】
  • 从后端研发角度出发,使用k8s部署业务系统
  • ARP协议【复习篇】
  • Tortoise-ORM级联查询与预加载性能优化
  • Nacos简介—3.Nacos的配置简介
  • 如何修改npm的全局安装路径?
  • 冲刺一区!挑战7天一篇文献计量学SCI DAY1-7
  • 机器之眼megauging(工业机器视觉软件)是否开源?
  • 【机器学习-线性回归-3】深入浅出:简单线性回归的概念、原理与实现
  • C#中常见的设计模式
  • 金融行业微服务架构设计与挑战 - Java架构师面试实战
  • 每日学习Java之一万个为什么?
  • IntelliJ IDEA 2025.2 和 JetBrains Rider 2025.1 恢复git commit为模态窗口
  • 前端开发中shell的使用场景
  • 一、鸿蒙编译篇
  • Jest 快照测试
  • 新电脑工作环境初始化2025
  • 硬件须知的基本问题1
  • JDK 17 与 Spring Cloud Gateway 新特性实践指南
  • nginx 504 (Gateway Time-out)
  • 三大交易所修订股票上市规则:明确关键少数责任,强化中小股东保障
  • 为国出征指纹却无法识别?他刷新了我军在这一项目的最好成绩
  • 贵州通报9起群众身边不正之风和腐败问题典型案例
  • 五矿地产:今年要确保债务“不爆雷”、交付“不烂尾”
  • 释新闻|印度宣布“掐断”巴基斯坦水源,对两国意味着什么?
  • 拉卡拉一季度净利约1亿降超五成,去年净利3.5亿降逾23%