【Compose】亲手封装一个简单灵活的下拉刷新上拉加载 Compose Layout

article/2025/10/12 20:11:34

Compose 的下拉刷新有现成的 Material 库可以直接使用,非常简单方便。

但是上拉加载目前没看到有封装的特别好的库,Paging 有些场景无法满足,而且上拉加载也是个比较简单的功能,没必要再去依赖一个质量未知的库。我们可以基于目前的 LazyList 简单的封装一个灵活的组件。

基本原则是仍然基于现有的 PullRefresh 以及 LazyList API 实现,不依赖三方库,使用简单灵活好用。

接口设计

首先我们将这个可以上拉加载下拉刷新的 Compose 函数命名为 LoadableLazyColumn

上面说到我们需要基于 PullRefresh 以及 LazyList API 实现,这两个组件都具备各自的 State。

  • PullRefreshState :下拉刷新的 State
  • LazyListState :LazyColumn 的 State

由于我们需要在此基础上提供上拉加载的能力,那还需要一个上拉加载的 State,我们可以将其命名为 LoadMoreState ,目前 LoadMoreState 需要包含两个参数:

  1. loadMoreRemainCountThreshold :加载更多的剩余 Item 个数阈值,当剩余个数小于等于这个阈值时开始发起加载更多请求。
  2. onLoadMore :加载更多的事件回调。

既然提供了 LoadMoreState ,我们还应该提供一个对应的 remember 函数。

@Composable
fun rememberLoadMoreState(loadMoreRemainCountThreshold: Int,onLoadMore: () -> Unit,
): LoadMoreState {return remember {LoadMoreState(loadMoreRemainCountThreshold, onLoadMore)}
}

上面我们只是单纯的定义了 LoadMoreState,同时我们也知道了 LoadableLazyColumn 还包含另外两个 State,总共也就是三个 State。

现在我们需要创建 LoadableLazyColumnState ,它需要包含上面说的三个 State。

@OptIn(ExperimentalMaterialApi::class)
data class LoadableLazyColumnState(val lazyListState: LazyListState,val pullRefreshState: PullRefreshState,val loadMoreState: LoadMoreState,
)

以及对应的 remember 方法。

不过上面说的三个 state 只是我们的内部实现,这不是调用者需要考虑的事情,对于使用者来说这只是一个 state,因此我们的 remember 方法的参数应该是这三个 state 的合集。

@Composable
@ExperimentalMaterialApi
fun rememberLoadableLazyColumnState(refreshing: Boolean,onRefresh: () -> Unit,onLoadMore: () -> Unit,refreshThreshold: Dp = PullRefreshDefaults.RefreshThreshold,refreshingOffset: Dp = PullRefreshDefaults.RefreshingOffset,loadMoreRemainCountThreshold: Int = 5,initialFirstVisibleItemIndex: Int = 0,initialFirstVisibleItemScrollOffset: Int = 0
): LoadableLazyColumnState {val pullRefreshState = rememberPullRefreshState(refreshing = refreshing,onRefresh = onRefresh,refreshingOffset = refreshingOffset,refreshThreshold = refreshThreshold,)val lazyListState = rememberLazyListState(initialFirstVisibleItemScrollOffset = initialFirstVisibleItemScrollOffset,initialFirstVisibleItemIndex = initialFirstVisibleItemIndex,)val loadMoreState = rememberLoadMoreState(loadMoreRemainCountThreshold, onLoadMore)return remember {LoadableLazyColumnState(lazyListState = lazyListState,pullRefreshState = pullRefreshState,loadMoreState = loadMoreState,)}
}

这样我们就创建了 LoadableLazyColumnState

然后 LoadableLazyColumn 这个函数的入参就显而易见了。

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun LoadableLazyColumn(modifier: Modifier = Modifier,state: LoadableLazyColumnState,refreshing: Boolean,loading: Boolean,contentPadding: PaddingValues = PaddingValues(0.dp),reverseLayout: Boolean = false,verticalArrangement: Arrangement.Vertical =if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,horizontalAlignment: Alignment.Horizontal = Alignment.Start,flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),userScrollEnabled: Boolean = true,loadingContent: (@Composable () -> Unit)? = null,content: LazyListScope.() -> Unit,
)

实现方案

这里会根据 LazyList 滑动事件来触发加载更多事件,当滑动事件结束后,判断用户是否为向下滑动,并且剩余元素的个数小于等于设定的阈值。

所幸 lazyListState 提供了这些状态,我们可以通过它那计算出上面的情况。

val lazyListState = state.lazyListState
// 获取 lazyList 布局信息
val listLayoutInfo by remember { derivedStateOf { lazyListState.layoutInfo } }

可以通过下面的方法获取到 LazyList 是否正在滑动:

// Whether this [ScrollableState] is currently scrolling by gesture, 
// fling or programmatically ornot.
lazyListState.isScrollInProgress

然后通过下面的两个方法获取到最后一个可见的 index,以及 item 总数:

listLayoutInfo.visibleItemsInfo.lastOrNull()?.index
listLayoutInfo.totalItemsCount

上面说的几个方法都是获取当前状态,但我们的目的是判断状态的变化,主要是下面两个事件变化:

  • 滑动停止事件
  • 最后一个可见 index 变化事件

如果我们能在滑动事件停止后判断最后一个可见 index 与上次滑动结束后的最后一个可见 index 相比的大小,就知道是向上滑动还是向下滑动了。再加上最后一个可见 index 与阈值相比,就可以判断触发加载更多事件了。

这里我们使用 remember 函数来实现,即 remember 上次的值,与当前值做对比。

// 上次是否正在滑动
var lastTimeIsScrollInProgress by remember {mutableStateOf(lazyListState.isScrollInProgress)
}
// 上次滑动结束后最后一个可见的index
var lastTimeLastVisibleIndex by remember {mutableStateOf(listLayoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0)
}
// 当前是否正在滑动
val currentIsScrollInProgress = lazyListState.isScrollInProgress
// 当前最后一个可见的 index
val currentLastVisibleIndex = listLayoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0

通过上面的代码我们就拿到了所有需要的状态了,然后简单对比一下即可。

if (!currentIsScrollInProgress && lastTimeIsScrollInProgress) {if (currentLastVisibleIndex != lastTimeLastVisibleIndex) {val isScrollDown = currentLastVisibleIndex > lastTimeLastVisibleIndexval remainCount = listLayoutInfo.totalItemsCount - currentLastVisibleIndex - 1if (isScrollDown && remainCount <= state.loadMoreState.loadMoreRemainCountThreshold) {LaunchedEffect(Unit) {state.loadMoreState.onLoadMore()}}}// 滑动结束后再更新值lastTimeLastVisibleIndex = currentLastVisibleIndex
}
lastTimeIsScrollInProgress = currentIsScrollInProgress

这样就差不多了,看下所有的代码。

package com.zhangke.framework.loadable.lazycolumnimport androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshDefaults
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.PullRefreshState
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp@OptIn(ExperimentalMaterialApi::class)
@Composable
fun LoadableLazyColumn(modifier: Modifier = Modifier,state: LoadableLazyColumnState,refreshing: Boolean,loading: Boolean,contentPadding: PaddingValues = PaddingValues(0.dp),reverseLayout: Boolean = false,verticalArrangement: Arrangement.Vertical =if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,horizontalAlignment: Alignment.Horizontal = Alignment.Start,flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),userScrollEnabled: Boolean = true,loadingContent: (@Composable () -> Unit)? = null,content: LazyListScope.() -> Unit,
) {val lazyListState = state.lazyListState// 获取 lazyList 布局信息val listLayoutInfo by remember { derivedStateOf { lazyListState.layoutInfo } }Box(modifier = modifier.pullRefresh(state.pullRefreshState)) {LazyColumn(contentPadding = contentPadding,state = state.lazyListState,reverseLayout = reverseLayout,verticalArrangement = verticalArrangement,horizontalAlignment = horizontalAlignment,flingBehavior = flingBehavior,userScrollEnabled = userScrollEnabled,content = {content()item {if (loadingContent != null) {loadingContent()} else {if (loading) {Box(modifier = Modifier.fillMaxWidth()) {CircularProgressIndicator(modifier = Modifier.size(30.dp).align(Alignment.Center))}}}}},)PullRefreshIndicator(refreshing,state.pullRefreshState,Modifier.align(Alignment.TopCenter))}// 上次是否正在滑动var lastTimeIsScrollInProgress by remember {mutableStateOf(lazyListState.isScrollInProgress)}// 上次滑动结束后最后一个可见的indexvar lastTimeLastVisibleIndex by remember {mutableStateOf(listLayoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0)}// 当前是否正在滑动val currentIsScrollInProgress = lazyListState.isScrollInProgress// 当前最后一个可见的 indexval currentLastVisibleIndex = listLayoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0if (!currentIsScrollInProgress && lastTimeIsScrollInProgress) {if (currentLastVisibleIndex != lastTimeLastVisibleIndex) {val isScrollDown = currentLastVisibleIndex > lastTimeLastVisibleIndexval remainCount = listLayoutInfo.totalItemsCount - currentLastVisibleIndex - 1if (isScrollDown && remainCount <= state.loadMoreState.loadMoreRemainCountThreshold) {LaunchedEffect(Unit) {state.loadMoreState.onLoadMore()}}}// 滑动结束后再更新值lastTimeLastVisibleIndex = currentLastVisibleIndex}lastTimeIsScrollInProgress = currentIsScrollInProgress
}@Composable
@ExperimentalMaterialApi
fun rememberLoadableLazyColumnState(refreshing: Boolean,onRefresh: () -> Unit,onLoadMore: () -> Unit,refreshThreshold: Dp = PullRefreshDefaults.RefreshThreshold,refreshingOffset: Dp = PullRefreshDefaults.RefreshingOffset,loadMoreRemainCountThreshold: Int = 5,initialFirstVisibleItemIndex: Int = 0,initialFirstVisibleItemScrollOffset: Int = 0
): LoadableLazyColumnState {val pullRefreshState = rememberPullRefreshState(refreshing = refreshing,onRefresh = onRefresh,refreshingOffset = refreshingOffset,refreshThreshold = refreshThreshold,)val lazyListState = rememberLazyListState(initialFirstVisibleItemScrollOffset = initialFirstVisibleItemScrollOffset,initialFirstVisibleItemIndex = initialFirstVisibleItemIndex,)val loadMoreState = rememberLoadMoreState(loadMoreRemainCountThreshold, onLoadMore)return remember {LoadableLazyColumnState(lazyListState = lazyListState,pullRefreshState = pullRefreshState,loadMoreState = loadMoreState,)}
}@Composable
fun rememberLoadMoreState(loadMoreRemainCountThreshold: Int,onLoadMore: () -> Unit,
): LoadMoreState {return remember {LoadMoreState(loadMoreRemainCountThreshold, onLoadMore)}
}data class LoadMoreState(val loadMoreRemainCountThreshold: Int,val onLoadMore: () -> Unit,
)@OptIn(ExperimentalMaterialApi::class)
data class LoadableLazyColumnState(val lazyListState: LazyListState,val pullRefreshState: PullRefreshState,val loadMoreState: LoadMoreState,
)

这就是所有代码了。

本文转自 [https://juejin.cn/post/7251231773672079416]

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓


http://chatgpt.dhexx.cn/article/h4P9qtFE.shtml

相关文章

如何实现上拉加载,下拉刷新?

如何实现上拉加载下拉刷新&#xff1f; 实现原理上拉加载下拉刷新 实现原理 上拉加载及下拉刷新都依赖于用户交互 最重要的是要理解在什么场景&#xff0c;什么时机下触发交互动作 上拉加载 首先可以看一张图 上拉加载的本质是页面触底&#xff0c;或者快要触底时的动作 判…

【前端知识之JS】如何实现上拉加载和下拉刷新

前言 本系列主要整理前端面试中需要掌握的知识点。本节介绍如何实现上拉加载和下拉刷新。 文章目录 前言一、介绍二、实现原理1、上拉加载2、下拉刷新 一、介绍 下拉刷新和上拉加载这两种交互方式通常出现在移动端中&#xff1b;本质上等同于PC网页中的分页&#xff0c;只是交…

大数据和人工智能到底是什么关系

大数据和人工智能的关系&#xff0c;首先要说什么是大数据。这些年来&#xff0c;大数据先是被神化&#xff0c;继而又被妖魔化&#xff0c;到了今天&#xff0c;其实谁也不知道别人所谓的大数据指的是什么。有时候大数据的定义里既有平台&#xff08;硬件&#xff09;又有分析…

大数据和人工智能AI的联系和区别

大数据和人工智能两个词是我们现在频繁听到的两个词汇&#xff0c;那么这两者之间到底有什么联系 首先我们要了解到什么是大数据&#xff1f;什么是人工智能&#xff1f;大数据就是一种规模大到在获取、存储、管理、分析方面大大超出了传统数据库软件工具能力范围的数据集合&…

人工智能、机器学习、深度学习 三者关系

目录 1、AI ML DL关系 2、发展历程 2.1、人工智能&#xff08;ArtificiaI Intelligence&#xff09; 2.2、机器学习&#xff08;Machine Learning&#xff09; 2.3、深度学习&#xff08;Deep Learning&#xff09; 1、AI ML DL关系 为了赋予计算机以人类的理解能力与逻辑…

云计算、大数据和人工智能三者到底什么关系

今天跟大家讲讲云计算、大数据和人工智能。为什么讲这三个东西呢&#xff1f;因为这三个东西现在非常火&#xff0c;并且它们之间好像互相有关系&#xff1a;一般谈云计算的时候会提到大数据、谈人工智能的时候会提大数据、谈人工智能的时候会提云计算……感觉三者之间相辅相成…

人工智能和大数据分析之间,主要有什么区别

首先来看看认知计算和人工智能的区别 人工智能的概念已经有二十多年&#xff0c;人工智能从历史和研究角度来讲主要目的是为了让机器表现的更像人类&#xff0c;我们称之为Intelligent Behavior。IBM的认知计算从技术角度上讲和AI是有很多共性的地方&#xff0c;比如机器学习&…

人工智能与大数据的完美结合

人工智能(AI)已经存在几十年了。然而&#xff0c;最近随着“大数据”的出现&#xff0c;它得到了越来越多的关注。维基百科对人工智能的释义如下: 在计算机科学中&#xff0c;人工智能研究的领域将自己定义为“智能代理AI和大数据&#xff1a;完美结合”的研究&#xff1a;任何…

大数据和人工智能的概念

大数据和人工智能的概念 概述大数据和人工智能领域的一些热门趋势。我们将看到世界如何通过数字化而改变&#xff0c;从而导致消费和工业领域的大数据现象&#xff1b;了解到数据量呈指数级增长&#xff0c;从太字节到艾字节再到泽字节&#xff1b;意识到计算机的处理能力增加…

云计算、大数据和人工智能之间的关系

大家好&#xff0c;这里是抖码课堂&#xff0c;抖码课堂专注提升互联网技术人的软硬实力。 这篇文章我们从下面的内容来聊下云计算、人工智能、大数据技术三者之间的关系 探讨什么是云计算&#xff0c;在这里分别探讨云计算的 IaaS、PaaS 以及 SaaS 探讨云计算和大数据技术之…

大数据属于人工智能吗?什么是大数据?

对于“大数据”(Big data)研究机构Gartner给出了这样的定义。“大数据”是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力来适应海量、高增长率和多样化的信息资产。大数据属于人工智能吗&#xff1f;本篇来解答一下这个问题。 大数据属于人工智能吗&#xff…

大数据和人工智能之间,主要有什么区别?

大数据vs.人工智能是一种公平的比较吗?在某种程度上&#xff0c;它是&#xff0c;但首先让我们先厘清它们之间的区别。 人工智能和大数据是人们耳熟能详的流行术语&#xff0c;但也可能会有一些混淆。人工智能和大数据有什么相似之处和不同之处?它们有什么共同点吗?它们是否…

AI和大数据的关系

近几年随着AlphaGO的骄人战绩&#xff0c;人工智能和大数据备受追捧&#xff0c;热度空前。而在实际接触中&#xff0c;大家对人工智能和大数据的认知普遍是“只知其名不知其意”&#xff0c;因此对企业而言&#xff0c;猎头的推荐也往往与岗位匹配度不高。那么被人们认为高深莫…

云计算、大数据和人工智能的关系

云计算、大数据和人工智能的关系 来源&#xff1a; 刘超&#xff08;popsuper1982&#xff09; https://www.cnblogs.com/popsuper1982/p/8505203.html 我今天要讲这三个话题&#xff0c;一个是云计算&#xff0c;一个大数据&#xff0c;一个人工智能&#xff0c;我为什么要讲…

大数据,云计算,人工智能之间的关系

一、云计算最初是实现资源管理的灵活性 我们首先来说云计算&#xff0c;云计算最初的目标是对资源的管理&#xff0c;管理的主要是计算资源&#xff0c;网络资源&#xff0c;存储资源三个方面。 1.1 管数据中心就像配电脑 什么叫计算&#xff0c;网络&#xff0c;存储资源呢&am…

关于大数据和人工智能发展的思考

10月12日&#xff0c;第七届中国智能产业高峰论坛在佛山开幕&#xff0c;在第一天的主论坛上&#xff0c;北京拓尔思信息技术股份有限公司副董事长、总裁施水才发表了主题为《大数据和人工智能发展的思考》的精彩演讲。 在演讲中&#xff0c;施水才先生从自身多年大数据技术和服…

大数据和人工智能的关系,超全解析

大数据拥抱云计算 在PaaS层中一个复杂的通用应用就是大数据平台。大数据是如何一步一步融入云计算的呢&#xff1f; 1数据不大也包含智慧 一开始这个大数据并不大。原来才有多少数据&#xff1f;现在大家都去看电子书&#xff0c;上网看新闻了&#xff0c;在我们80后小时候&…

云计算、大数据、人工智能的关系

云计算,大数据,人工智能以及他们的关系。 一、云计算最初是实现资源管理的灵活性 我们首先来说云计算,云计算最初的目标是对资源的管理,管理的主要是计算,存储,网络资源。 1.1 管数据中心就像配电脑 什么叫计算,存储,网络资源呢?就说你要买台笔记本电脑吧,你是不是…

云计算、大数据和人工智能之间的关系----详细说明

今天跟大家讲讲云计算、大数据和人工智能。为什么讲这三个东西呢&#xff1f;因为这三个东西现在非常火&#xff0c;并且它们之间好像互相有关系&#xff1a;一般谈云计算的时候会提到大数据、谈人工智能的时候会提大数据、谈人工智能的时候会提云计算……感觉三者之间相辅相成…

AI与大数据的关系

最近在忙着专业分流的事情&#xff0c;自己纠结的专业主要就是人工智能和大数据&#xff0c;找了很多资料&#xff0c;终于整理出二者的关系。 原文地址&#xff1a;https://www.sohu.com/a/224177824_764294 更专业一些的分析可以看这篇文章&#xff1a;https://blog.csdn.ne…