Jetpack Compose 的高性能下拉刷新与上拉加载更多组件
GitHub:compose-refresh-layout
基于 Jetpack Compose 构建的声明式嵌套滑动刷新容器,提供极简、高性能的下拉刷新与上拉加载能力。其核心使用原生 graphicsLayer 进行零重绘渲染,具备丝滑的物理阻尼回弹效果。
运行效果

环境要求
- Kotlin 1.9+
- Jetpack Compose 1.5.0+
- minSdk 24
安装
在模块的 build.gradle.kts 中添加依赖:
scss
dependencies {
implementation("io.github.lx-0713:compose-smart-refresh:1.0.1")
}
确保 settings.gradle.kts 包含 mavenCentral() 仓库:
scss
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
基础用法
用 SmartSwipeRefresh 包裹任意支持嵌套滚动的组件(LazyColumn、LazyRow 等),通过 rememberSmartSwipeRefreshState() 获取状态机,在回调中执行业务逻辑后调用 state.finish() 结束即可。
scss
@Composable
fun BasicDemo() {
val state = rememberSmartSwipeRefreshState()
val scope = rememberCoroutineScope()
var items by remember { mutableStateOf((1..20).toList()) }
SmartSwipeRefresh(
state = state,
onRefresh = {
scope.launch {
delay(1500) // 模拟网络请求
items = (1..20).toList()
state.finish() // 结束刷新
}
},
onLoadMore = {
scope.launch {
delay(1500)
if (items.size >= 60) {
state.finish(noMoreData = true) // 无更多数据
} else {
items = items + (items.size + 1..items.size + 20)
state.finish()
}
}
}
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items) { item ->
Text("Item $item", modifier = Modifier.padding(16.dp))
}
}
}
}
注意 :
onRefresh和onLoadMore回调都在主线程触发,必须通过scope.launch {}切换到协程中执行异步操作,并在完成后调用state.finish()。
横向列表
传入 orientation = Orientation.Horizontal 即可切换为横向模式,无需任何其他改动。Header 出现在列表左侧(向右拉触发刷新),Footer 出现在右侧(向左拉触发加载)。
ini
SmartSwipeRefresh(
state = state,
orientation = Orientation.Horizontal,
onRefresh = { scope.launch { delay(1000); state.finish() } },
onLoadMore = { scope.launch { delay(1000); state.finish() } }
) {
LazyRow(modifier = Modifier.fillMaxSize()) {
items(20) { index ->
Text("Item $index", modifier = Modifier.padding(16.dp))
}
}
}
自定义"无更多数据"文案
通过 noMoreDataText 参数替换默认底部文案。当 state.finish(noMoreData = true) 被调用后,Footer 会自动切换到无数据停靠状态并展示该文案:
ini
SmartSwipeRefresh(
state = state,
noMoreDataText = "--- 我可是有底线的 ---",
onLoadMore = {
scope.launch {
delay(1000)
state.finish(noMoreData = true)
}
}
) { ... }
自定义 Header / Footer
通过 header 和 footer 参数传入自定义 Composable。在自定义视图中,可通过 state.refreshFlag、state.loadMoreFlag、state.noMoreData 感知当前交互状态并做出响应:
scss
SmartSwipeRefresh(
state = state,
header = {
Box(
modifier = Modifier.fillMaxWidth().height(60.dp),
contentAlignment = Alignment.Center
) {
when (state.refreshFlag) {
SmartRefreshFlag.PULLING -> Text("继续下拉刷新...")
SmartRefreshFlag.REFRESHING -> CircularProgressIndicator()
else -> Text("下拉刷新")
}
}
},
footer = {
Box(
modifier = Modifier.fillMaxWidth().height(50.dp),
contentAlignment = Alignment.Center
) {
if (state.noMoreData) {
Text("没有更多了", color = Color.Gray)
} else {
Row(verticalAlignment = Alignment.CenterVertically) {
CircularProgressIndicator(modifier = Modifier.size(16.dp))
Spacer(modifier = Modifier.width(8.dp))
Text("加载中...")
}
}
}
}
) {
LazyColumn { /* ... */ }
}
提示 :自定义 Header/Footer 的高度决定了拉动阈值,建议保持在
48.dp ~ 64.dp之间以获得最佳手感。
程序主动触发刷新 / 加载
无需用户手动操作,可由代码主动驱动刷新行为:
scss
// 进入页面时自动触发下拉刷新(Header 弹出)
LaunchedEffect(Unit) {
state.autoRefresh()
}
// 程序主动触发上拉加载(Footer 弹出)
LaunchedEffect(Unit) {
state.autoLoadMore()
}
autoRefresh()和autoLoadMore()内部均有幂等保护,重复调用不会产生副作用。
动态开关与阻尼调节
所有控制属性均支持运行时动态修改,Compose 会自动响应变化:
ini
// 进入编辑模式时禁用下拉刷新
state.enableRefresh = false
// 数据请求中临时禁用上拉加载,防止重复触发
state.enableLoadMore = false
// 调节拖拽阻尼(0.1~1f,越小手感越紧致,默认 0.5f)
state.stickinessLevel = 0.3f
切换条件后重置无更多数据
切换搜索关键词、筛选条件或 Tab 后,需要重置底部状态以重新激活上拉加载:
scss
// 搜索关键词变化时重置
LaunchedEffect(keyword) {
state.resetNoMoreData()
}
执行下拉刷新(
onRefresh触发)时,noMoreData会自动重置,无需手动调用。
仅启用单向刷新
若只需要下拉刷新,不传 onLoadMore 即可;若只需要上拉加载,不传 onRefresh 即可。也可以通过属性动态控制:
ini
// 只有下拉刷新
SmartSwipeRefresh(
state = state,
onRefresh = { scope.launch { delay(1000); state.finish() } }
// 不传 onLoadMore,上拉加载自动禁用
) { ... }
// 运行时关闭某一端
state.enableLoadMore = false
API 速查
SmartSwipeRefresh 参数
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
state |
SmartSwipeRefreshState |
核心状态机(必填) | 无 |
orientation |
Orientation |
Vertical 或 Horizontal |
Vertical |
onRefresh |
(() -> Unit)? |
下拉刷新回调 | null |
onLoadMore |
(() -> Unit)? |
上拉加载回调 | null |
noMoreDataText |
String? |
自定义无更多数据文案 | null |
header |
@Composable () -> Unit |
自定义头部 Composable | RefreshHeader |
footer |
@Composable () -> Unit |
自定义尾部 Composable | RefreshFooter |
content |
@Composable () -> Unit |
列表内容(必填) | 无 |
SmartSwipeRefreshState 方法与属性
| 方法 / 属性 | 说明 |
|---|---|
finish(noMoreData: Boolean = false) |
结束当前刷新或加载操作(自动识别类型) |
autoRefresh() |
程序主动触发下拉刷新,Header 自动弹出 |
autoLoadMore() |
程序主动触发上拉加载,Footer 自动弹出 |
resetNoMoreData() |
重置无更多数据状态 |
enableRefresh |
动态开关下拉刷新 |
enableLoadMore |
动态开关上拉加载 |
stickinessLevel |
拖拽阻尼系数(0.1~1f,默认 0.5f) |
refreshFlag |
顶部当前状态(SmartRefreshFlag) |
loadMoreFlag |
底部当前状态(SmartRefreshFlag) |
noMoreData |
是否已无更多数据 |
SmartRefreshFlag 枚举
| 值 | 含义 |
|---|---|
IDLE |
空闲,无越界偏移 |
PULLING |
正在拉拽,尚未达到触发阈值,松手后自动复位 |
REFRESHING |
已触发业务回调,等待 finish() 指令 |
FINISHING |
归位动画执行中,期间屏蔽额外输入 |