当前位置: 首页 > news >正文

Compose笔记(四十八)--PullRefresh

        这一节主要了解一下Compose中的PullRefresh ,在Jetpack Compose开发中,PullRefresh是用于实现下拉刷新功能的组件,它属于androidx.compose.material.pullrefresh包,通过检测用户的下拉手势触发刷新操作。

API
state:PullRefreshState:管理下拉刷新状态的对象,包含刷新状态、进度等信息
onRefresh:()->Unit:触发刷新时的回调函数,通常在这里执行数据加载逻辑
modifier:Modifier:修饰符
enabled:Boolean:是否启用下拉刷新功能
content:@Composable()-> Unit:需要添加下拉刷新功能的内容

使用场景
1 数据列表刷新,社交媒体的动态列表,用户下拉获取最新内容;新闻应用的新闻流,确保数据实时性。
2 自定义交互体验,需要自定义刷新动画,动态控制刷新手势。

栗子:

gradle添加依赖:

  implementation("androidx.compose.material:material:1.5.4")implementation("androidx.compose.material:material-icons-extended:1.5.4")
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PullRefreshExample() {var items by remember { mutableStateOf(List(20) { "初始项目 ${it + 1}" }) }var refreshing by remember { mutableStateOf(false) }val curScope = rememberCoroutineScope()val pullRefreshState = rememberPullRefreshState(refreshing = refreshing,onRefresh = {refreshing = truecurScope.launch(Dispatchers.IO) {delay(2000) items = List(20) { "刷新后的项目 ${it + 1} (${System.currentTimeMillis() % 1000})" }refreshing = false}})Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState) ) {LazyColumn(modifier = Modifier.fillMaxSize()) {items(items) { item ->Text(text = item,modifier = Modifier.padding(20.dp))}}PullRefreshIndicator(refreshing = refreshing,state = pullRefreshState,modifier = Modifier.align(Alignment.TopCenter))}
}
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PullRefreshDemo() {var newsItems by remember { mutableStateOf<List<String>>(emptyList()) }    var isRefreshing by remember { mutableStateOf(false) }   var statusText by remember { mutableStateOf("下拉刷新获取最新资讯") }val curScope = rememberCoroutineScope()LaunchedEffect(Unit) {loadInitialData {newsItems = it}}val pullRefreshState = rememberPullRefreshState(refreshing = isRefreshing,onRefresh = {isRefreshing = truestatusText = "正在加载最新资讯..."curScope.launch(Dispatchers.IO) {delay(2500) // 模拟2.5秒加载时间val newItems = List(15) {"最新资讯 ${it + 1} (${System.currentTimeMillis() % 10000})"}newsItems = newItemsstatusText = "刷新完成,共 ${newItems.size} 条资讯"isRefreshing = falsedelay(3000)statusText = "下拉刷新获取最新资讯"}})Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) {LazyColumn(modifier = Modifier.fillMaxSize()) {if (newsItems.isEmpty() && !isRefreshing) {item {Text(text = "加载中...",modifier = Modifier.fillMaxSize().padding(20.dp),textAlign = TextAlign.Center)}} else {items(newsItems) { news ->Text(text = news,style = MaterialTheme.typography.body1,modifier = Modifier.padding(16.dp))}}}PullRefreshIndicator(refreshing = isRefreshing,state = pullRefreshState,modifier = Modifier.align(Alignment.TopCenter))Text(text = statusText,modifier = Modifier.align(Alignment.TopCenter).padding(top = 50.dp),color = MaterialTheme.colors.primary)}
}private suspend fun loadInitialData(callback: (List<String>) -> Unit) {delay(1500) val initialData = List(15) { "初始资讯 ${it + 1}" }callback(initialData)
}

注意
1 刷新操作应在协程中执行,避免阻塞UI线程
2 确保内容是可滚动的
3 可以通过PullRefreshIndicator的参数自定义指示器样式

源码:
1.PullRefreshState
这是管理下拉刷新状态的核心类,关键属性和方法:

class PullRefreshState(// 当前刷新状态val refreshing: Boolean,// 下拉进度val progress: Float,// 内部用于更新状态的回调private val onRefresh: () -> Unit,// 刷新阈值private val refreshThreshold: Float,// 最大下拉距离internal val threshold get() = _threshold
) {// 处理拖动逻辑的内部方法internal fun onPull(pullDelta: Float): Float { ... }// 处理拖动结束internal fun onRelease(): Boolean { ... }
}

分析:
progress:实时反映下拉距离与阈值的比例
onPull:根据手势拖动距离更新progress,返回实际消耗的拖动距离
onRelease:松手时判断是否触发刷新

2.rememberPullRefreshState
用于创建并记忆 PullRefreshState 的函数,关联刷新状态和回调:

@Composable
fun rememberPullRefreshState(refreshing: Boolean,onRefresh: () -> Unit,refreshThreshold: Dp = 80.dp,maxDrag: Dp = 120.dp
): PullRefreshState {val density = LocalDensity.currentval thresholdPx = with(density) { refreshThreshold.toPx() }val maxDragPx = with(density) { maxDrag.toPx() }return remember(refreshing) {PullRefreshState(refreshing = refreshing,onRefresh = onRefresh,refreshThreshold = thresholdPx,maxDrag = maxDragPx)}
}

3 pullRefresh修饰符是实现下拉检测的核心,其内部通过pointerInput处理触摸事件:

ExperimentalMaterialApi
fun Modifier.pullRefresh(onPull: (pullDelta: Float) -> Float,// 处理下拉距离的回调onRelease: suspend (flingVelocity: Float) -> Float,// 处理松手速度的回调enabled: Boolean = true,// 是否启用下拉刷新
) = nestedScroll(PullRefreshNestedScrollConnection(onPull, onRelease, enabled))private class PullRefreshNestedScrollConnection(private val onPull: (pullDelta: Float) -> Float,private val onRelease: suspend (flingVelocity: Float) -> Float,private val enabled: Boolean,
) : NestedScrollConnection {override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset =when {!enabled -> Offset.Zero  // 禁用时不处理source == NestedScrollSource.UserInput && available.y < 0 ->Offset(0f, onPull(available.y)) // Swiping up 传递上拉距离给 onPullelse -> Offset.Zero}override fun onPostScroll(consumed: Offset,available: Offset,source: NestedScrollSource,): Offset =when {!enabled -> Offset.Zerosource == NestedScrollSource.UserInput && available.y > 0 ->Offset(0f, onPull(available.y)) // Pulling down 传递下拉距离给 onPullelse -> Offset.Zero}override suspend fun onPreFling(available: Velocity): Velocity {return Velocity(0f, onRelease(available.y))  // 传递垂直速度给 onRelease}
}

分析:3.1事件过滤:仅处理用户输入(NestedScrollSource.UserInput)的滚动事件,忽略其他来源(如程序触发的滚动)。
3.2方向区分:
3.2.1上滑(available.y < 0)通过onPreScroll处理,通常用于取消刷新状态。
3.2.2下拉(available.y > 0)通过onPostScroll处理,是触发下拉刷新的核心路径。
3.3数据传递:将滚动距离(pullDelta)和速度(flingVelocity)传递给外部回调,由外部处理状态更新(如下拉进度、是否触发刷新)。
3.4禁用控制:enabled参数为false时,所有方法返回Offset.Zero或空实现,不处理任何事件。

4 刷新指示器(PullRefreshIndicator)
指示器的UI和动画由PullRefreshIndicator实现,核心逻辑是根据PullRefreshState的progress和refreshing状态更新UI:

@Composable
fun PullRefreshIndicator(refreshing: Boolean,state: PullRefreshState,modifier: Modifier = Modifier,// 其他样式参数...
) {// 1. 计算指示器位置和大小动画val progress = state.progress.coerceIn(0f, 1f)val indicatorSize by animateDpAsState(targetValue = if (refreshing) 40.dp else 36.dp * (1f + progress),animationSpec = spring(stiffness = Spring.StiffnessMedium))// 2. 旋转动画(刷新时)val rotation by animateFloatAsState(targetValue = if (refreshing) 360f * 2 else 0f,animationSpec = infiniteRepeatable(animation = tween(1000, easing = LinearEasing)),label = "Rotation")// 3. 绘制指示器Box(modifier = modifier.size(indicatorSize)// 其他样式设置...) {CircularProgressIndicator(progress = if (refreshing) 1f else progress,rotationAngle = rotation,// 样式参数...)}
}

分析:
下拉过程中:根据progress缩放指示器大小,同步进度条
刷新中:显示无限旋转动画,固定指示器大小

简单总结:
初始化:通过rememberPullRefreshState创建状态对象,关联refreshing状态和onRefresh回调
手势监听:pullRefresh修饰符检测到下拉手势,判断内容是否在顶部
状态更新:下拉时实时更新progress,松手时若达到阈值则触发onRefresh
UI反馈:PullRefreshIndicator根据progress和refreshing状态显示对应的动画和进度


文章转载自:

http://cnM8JAyr.bnhyd.cn
http://uPMLCZNg.bnhyd.cn
http://1KkZo4A6.bnhyd.cn
http://CqJTXOBC.bnhyd.cn
http://HByYNauz.bnhyd.cn
http://MqTYzz8n.bnhyd.cn
http://4np2dGEm.bnhyd.cn
http://DJOVHNuS.bnhyd.cn
http://UQlhvvwG.bnhyd.cn
http://p94VVjGr.bnhyd.cn
http://7Yxc4UOS.bnhyd.cn
http://RnBc19Uv.bnhyd.cn
http://fRiWEbsV.bnhyd.cn
http://ynBNWsS7.bnhyd.cn
http://1g5doKHz.bnhyd.cn
http://77jRcD0x.bnhyd.cn
http://QFWSucBn.bnhyd.cn
http://GzCsQEay.bnhyd.cn
http://Ay95untf.bnhyd.cn
http://oX6Tp6nL.bnhyd.cn
http://fGrMEBLd.bnhyd.cn
http://ViZeJXgD.bnhyd.cn
http://JUvock2m.bnhyd.cn
http://eFRajRRI.bnhyd.cn
http://9zK8YYEv.bnhyd.cn
http://4tr4zHY1.bnhyd.cn
http://AOIUH4GF.bnhyd.cn
http://uIWBQiK2.bnhyd.cn
http://4Fp5Qjv9.bnhyd.cn
http://o4DbPz6c.bnhyd.cn
http://www.dtcms.com/a/370296.html

相关文章:

  • 性能优化的边界-不该优化什么
  • Qt串口通信学习
  • 云手机运行流畅,秒开不卡顿
  • Spring Boot中MyBatis的定义与使用
  • MQTT 与 Java 框架集成:Spring Boot 实战(二)
  • 使用Shell脚本实现Linux系统资源监控邮件告警
  • 提示词工程知识积累及分析
  • Excel 表格 - Excel 收起与展开工具栏
  • ElemenetUI之常用小组件
  • 【c++】函数重载
  • 算法复杂度分析:从理论基础到工程实践的系统认知
  • Java-118 深入浅出 MySQL ShardingSphere 分片剖析:SQL 支持范围、限制与优化实践
  • 小智医疗:Java大模型应用项目全流程实战
  • DeepSeek辅助在64位Linux中编译运行32位的asm-xml-1.4程序
  • Claude 中国禁用后,阿里 1T 参数模型 Qwen3-Max 连夜发布,效果太强了
  • C++并发编程指南 std::promise 介绍与使用
  • 使用函数调用对整形数组进行排序
  • Linux bzip2 命令使用说明
  • python打包工具setuptools
  • 屏幕小管家——图像识别自动操作助手
  • hbuilderX的gite项目怎么看项目地址
  • 【MFC】对话框节点属性:Language(语言)
  • 联邦学习论文分享:Towards Building the Federated GPT:Federated Instruction Tuning
  • 【Neovim】Vi、Vim、Neovim 与 LazyVim:发展史
  • Eigen中Eigen::Affine3d和Eigen::Isometry3d详解
  • 得物前端二面面经总结
  • 如何离线安装 VirtualMachinePlatform
  • Redisson分布式事务锁
  • 浪潮CD1000-移动云电脑-RK3528芯片-2+32G-安卓9-2种开启ADB ROOT刷机教程方法
  • 详解文件操作