Compose基于LazyColumn的上拉加载

文章介绍了如何在JetpackCompose中通过LazyColumn实现加载更多的功能,利用key和状态值控制加载逻辑。核心代码使用derivedStateOf监听列表状态,当滑动到最后一项时触发加载更多操作。同时,文章提到了网络状态管理类MMZLoadState和数据请求的处理方法。

前言:首先pagging对于compose是有支持的,基础需求已可以满足,但是因为存在这样那样的情况导致我不再使用它,而是自己弄了个出来,目前使用比较满意。

一句话总结:通过LazyColumn实现,并且简单的通过key来判定是否到了最后一行,以及通过状态值来控制显示与进行网络请求。

例子:

LazyColumn(modifier = Modifier
    .fillMaxSize()
    .background(MMZTheme.colors.cardBackColor)
    .drawListHeader(),
    state = listState,
    verticalArrangement = Arrangement.spacedBy(8.dp),
    content = {
        if (mViewModel.data.isNotEmpty()) {
            itemsIndexed(mViewModel.data) { _, item ->
                CabbagePriceCompose(
                    modifier = Modifier.fillMaxWidth(),
                    discountEntity = item
                ) {
                    NavigationUtil.jumpToProductDiscounts(mViewModel,"${item.id}")
                }
            }
            item(key = LoadMoreKey) {
                LoadMoreMonitorCompose(
                    listState = { listState },
                    loadState = { mViewModel.mmzLoadState }
                ) {
                    mViewModel.getData(false)
                }
            }
        }
    })

上述已经是一个完整的使用上拉加载的例子了。是不是超级简单? 核心就只有listState与LoadMoreKey,并且使用它将LazyColumn与LoadMoreMonitorCompose关联起来,其余的东西都不用太过关心。LoadMoreKey可以是任意非重复值

实现过程:

@Composable
fun LoadMoreMonitorCompose(
    modifier: Modifier = Modifier,
    listState: () -> LazyListState,
    loadState: () -> MMZLoadState,
    errorHint: String = "没有更多了",
    onLoadMore: () -> Unit
) {
    val lazyListState = listState()
    val mmzLoadState = loadState()

    val loadMore = remember {
        derivedStateOf {
            (lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.key == LoadMoreKey) &&
                    mmzLoadState.refreshState == NetState.SUCCESS &&
                    (mmzLoadState.loadState == NetState.IDLE || mmzLoadState.loadState == NetState.SUCCESS)
        }
    }

    LaunchedEffect(loadMore) {
        snapshotFlow { loadMore.value }
            .distinctUntilChanged()
            .collect {
                if (it) {
                    mmzLoadState.loadState = NetState.LOADING //相当于 加锁
                    onLoadMore()
                }
            }
    }

    Row(
        modifier = modifier
            .fillMaxWidth()
            .height(40.dp)
            .noRepeatClick {
                if (mmzLoadState.loadState != NetState.LOADING) {
                    onLoadMore()
                }
            },
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        when (mmzLoadState.loadState) {
            NetState.LOADING -> {
                CommonLoading()
            }
            NetState.NO_DATA -> {
                Text(
                    text = errorHint,
                    fontSize = 14.sp,
                    modifier = Modifier.padding(top = 5.dp, bottom = 5.dp),
                    color = MMZTheme.colors.thirdLeveTextColor
                )
            }
            NetState.ERROR -> {
                Text(
                    text = "加载错误,点击重试",
                    fontSize = 14.sp,
                    modifier = Modifier.padding(top = 5.dp, bottom = 5.dp),
                    color = MMZTheme.colors.thirdLeveTextColor
                )
            }
            else -> {
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxHeight()
                )
            }
        }
    }
}

此为核心代码,通过derivedStateOf对列表进行监听,当列表滑动至最后一个时,启动加载更多回调。 扩展性很强,可以任意修改加载更多的样式,如果想要预加载,则不能使用key的方式,而是通过数量来进行判定,只需要修改derivedStateOf内部的判断条件即可。

一些其余的内容:

class MMZLoadState {
    var refreshState by mutableStateOf(NetState.IDLE)
    var loadState by mutableStateOf(NetState.IDLE)
}

用于辅助网络请求。将请求分为了下拉刷新和加载更多

enum class NetState {
    LOADING, SUCCESS, ERROR, NO_DATA,IDLE
}

此为常见的网络状态,  加载中、成功、错误、没有数据、空闲

三者请求,即为一个完整体

一个网络访问的例子:

val mmzLoadState = MMZLoadState()
val data = mutableStateListOf<DiscountEntity>()

var endId =0L
fun getData(isRefresh:Boolean){
    viewModelScope.launch {
        if (isRefresh){
            endId =0
        }
        request { apiService.getCabbagePriceData(mapOf("num" to "10","id" to "$endId")) }.collect{ it ->
            if (it.netState==NetState.SUCCESS&&it.data?.discountList.isNullOrEmpty()){
                it.netState=NetState.NO_DATA
            }
            MMZUtil.formatMMZLoadState(mmzLoadState,isRefresh,it.netState)

            it.data?.discountList?.let {
                if (it.isNotEmpty()){
                    endId = it.lastOrNull()?.id?:0L
                    if (isRefresh){
                        data.clear()
                    }
                    data.addAll(it)
                }
            }
        }
    }
}
fun formatMMZLoadState(mmzLoadState: MMZLoadState, isRefresh: Boolean, netState: NetState) {
    if (isRefresh) {
        mmzLoadState.refreshState = netState
        mmzLoadState.loadState = NetState.IDLE
    } else {
        mmzLoadState.loadState = netState
    }
}

解释:此处使用formatMMZLoadState 是因为上述将两个状态进行了分开,但是在下拉刷新后,上推加载的状态应该跟恢复,如果只是做下拉加载的话,则可以完全忽略这里。

并且,你应该在判定数据不为空的前提下去加载条目 与 先赋值网络状态,再处理数据

以免因为自动重组 以及提前的加载更多Compose占位而导致的错误ui效果

总结:极其少量的代码去做了加载更多的事情,与传统的加载要去弄ui不同,这里只需要监测key即可,且对代码的入侵性极小,需要加载更多的时候提供一个listState与loadState即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值