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





