Jetpack Compose 的高性能下拉刷新与上拉加载更多组件

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 包裹任意支持嵌套滚动的组件(LazyColumnLazyRow 等),通过 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))
            }
        }
    }
}

注意onRefreshonLoadMore 回调都在主线程触发,必须通过 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)
        }
    }
) { ... }

通过 headerfooter 参数传入自定义 Composable。在自定义视图中,可通过 state.refreshFlagstate.loadMoreFlagstate.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 VerticalHorizontal 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 归位动画执行中,期间屏蔽额外输入
相关推荐
李斯维2 天前
Android 沉浸式(Edge-to-Edge)的介绍与应用
android·android jetpack
我命由我123452 天前
Jetpack Compose - 设置 Compose 编译器、设置 Compose 依赖项
android·java·java-ee·kotlin·android jetpack·android-studio·android runtime
帅次3 天前
深入 MaterialTheme:掌握 ColorScheme 与 Typography 的设计核心
android·kotlin·gradle·android jetpack·compose
simplepeng3 天前
如何减少 89% 的重组,每个Compose开发者都需要的技巧 - derivedStateOf
android·android jetpack
帅次3 天前
Navigation Compose:NavHost、NavController 与参数
android·kotlin·gradle·android jetpack·compose
赏金术士4 天前
JetPack Compose 弹窗、菜单、交互组件(五)
android·kotlin·交互·android jetpack·compose
赏金术士4 天前
JetPack Compose 基础核心模块(一)
android·kotlin·android jetpack·compose
alexhilton5 天前
如何用Perfetto来对启动优化去伪存真
android·kotlin·android jetpack