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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值