Jetpack Compose 的高性能下拉刷新与上拉加载更多组件
GitHub:compose-refresh-layout
基于 Jetpack Compose 构建的声明式嵌套滑动刷新容器,提供极简、高性能的下拉刷新与上拉加载能力。其核心使用原生 graphicsLayer 进行零重绘渲染,具备丝滑的物理阻尼回弹效果。
运行效果
环境要求
- Kotlin 1.9+
- Jetpack Compose 1.5.0+
- minSdk 24
安装
在模块的 build.gradle.kts 中添加依赖:
dependencies {
implementation("io.github.lx-0713:compose-smart-refresh:1.0.1")
}
确保 settings.gradle.kts 包含 mavenCentral() 仓库:
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
基础用法
用 SmartSwipeRefresh 包裹任意支持嵌套滚动的组件(LazyColumn、LazyRow 等),通过 rememberSmartSwipeRefreshState() 获取状态机,在回调中执行业务逻辑后调用 state.finish() 结束即可。
@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 出现在右侧(向左拉触发加载)。
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 会自动切换到无数据停靠状态并展示该文案:
SmartSwipeRefresh(
state = state,
noMoreDataText = "— 我可是有底线的 —",
onLoadMore = {
scope.launch {
delay(1000)
state.finish(noMoreData = true)
}
}
) { ... }
自定义 Header / Footer
通过 header 和 footer 参数传入自定义 Composable。在自定义视图中,可通过 state.refreshFlag、state.loadMoreFlag、state.noMoreData 感知当前交互状态并做出响应:
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之间以获得最佳手感。
程序主动触发刷新 / 加载
无需用户手动操作,可由代码主动驱动刷新行为:
// 进入页面时自动触发下拉刷新(Header 弹出)
LaunchedEffect(Unit) {
state.autoRefresh()
}
// 程序主动触发上拉加载(Footer 弹出)
LaunchedEffect(Unit) {
state.autoLoadMore()
}
autoRefresh()和autoLoadMore()内部均有幂等保护,重复调用不会产生副作用。
动态开关与阻尼调节
所有控制属性均支持运行时动态修改,Compose 会自动响应变化:
// 进入编辑模式时禁用下拉刷新
state.enableRefresh = false
// 数据请求中临时禁用上拉加载,防止重复触发
state.enableLoadMore = false
// 调节拖拽阻尼(0.1~1f,越小手感越紧致,默认 0.5f)
state.stickinessLevel = 0.3f
切换条件后重置无更多数据
切换搜索关键词、筛选条件或 Tab 后,需要重置底部状态以重新激活上拉加载:
// 搜索关键词变化时重置
LaunchedEffect(keyword) {
state.resetNoMoreData()
}
执行下拉刷新(
onRefresh触发)时,noMoreData会自动重置,无需手动调用。
仅启用单向刷新
若只需要下拉刷新,不传 onLoadMore 即可;若只需要上拉加载,不传 onRefresh 即可。也可以通过属性动态控制:
// 只有下拉刷新
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 | 归位动画执行中,期间屏蔽额外输入 |