Compose 列表刷新自定义动画效果

Compose 列表刷新自定义样式

效果如下

想过很多方案效果不太理想,最主要是手势触摸下拉上拉事件冲突,滑动偏移量不准。目前这种达到了效果,细节扩展还需自行实践

计算滑动的Touch事件捕捉监听

  • filter为 PointerEventType.Move触摸事件类型
  • PointerEventType.PressPointerEventType.MovePointerEventType.Release对应按下、移动和释放
  • offsetY计算滑动距离,距离差值过大所以除几倍到正常范围
  • isStart控制动画
ini 复制代码
.pointerInput(filter) {
    awaitPointerEventScope {
        while (true) {
            val event = awaitPointerEvent()
            // handle pointer event Press->Move->Release
            println("${event.type}")
            when (event.type) {
                PointerEventType.Press -> {
                    initY = event.changes.first().position.y
                    isStart = true
                }

                PointerEventType.Move -> {
                    offsetY = event.changes.first().position.y - initY
                }

                PointerEventType.Release -> {
                    offsetY = 0f;
                    isStart = false
                }
            }
        }
    }
}

留出空白动画区域

  • drawBehind绘制下拉弧度和椭圆
ini 复制代码
.drawBehind {
    val path = Path()
    var h = offsetY.div(3)
    path.moveTo(0f, 0f)
    path.cubicTo(
        0f,
        0f,
        size.width / 2,
        h,
        size.width,
        0f
    )
    drawPath(path, primaryColor)
    val s = Size(size.width.div(3), h)
    drawOval(
        Color.White,
        topLeft = Offset(size.width.div(2) - s.width.div(2), 4.dp.value),
        size = s
    )
}

compose lottie库使用

  • 布局里做一个高度变化的gif
ini 复制代码
LottieAnimation(
    composition = composition3,
    progress = { lottieAnimatable.value },
    modifier = Modifier
        .fillMaxWidth()
        .height(
            offsetY
                .div(24).dp
        )
)

完整代码

scss 复制代码
@Composable
private fun LogPointerEvents() {
    val filter = PointerEventType.Move
    val state = rememberLazyListState()
    var offsetY by remember {
        mutableFloatStateOf(0f)
    }
    var initY by remember {
        mutableFloatStateOf(0f)
    }
    val primaryColor = MaterialTheme.colorScheme.primary
    val composition3 by rememberLottieComposition(LottieCompositionSpec.Asset("Polite Chicky.json"))
    val lottieAnimatable = rememberLottieAnimatable()
    var isStart by remember {
        mutableStateOf(false)
    }
    if (isStart) {
        LaunchedEffect(Unit) {
            lottieAnimatable.animate(
                composition3,
                iterations = LottieConstants.IterateForever,
                clipSpec = LottieClipSpec.Progress(0.5f, 0.75f),
            )
        }
    }
    Column(
        Modifier
            .fillMaxSize()
            .background(Color(0xFFfefefe))
            .pointerInput(filter) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent()
                        // handle pointer event Press->Move->Release
                        println("${event.type}")
                        when (event.type) {
                            PointerEventType.Press -> {
                                initY = event.changes.first().position.y
                                isStart = true
                            }

                            PointerEventType.Move -> {
                                offsetY = event.changes.first().position.y - initY
                            }

                            PointerEventType.Release -> {
                                offsetY = 0f;
                                isStart = false
                            }
                        }
                    }
                }
            }
            .drawBehind {
                val path = Path()
                var h = offsetY.div(3)
                path.moveTo(0f, 0f)
                path.cubicTo(
                    0f,
                    0f,
                    size.width / 2,
                    h,
                    size.width,
                    0f
                )
                drawPath(path, primaryColor)
                val s = Size(size.width.div(3), h)
                drawOval(
                    Color.White,
                    topLeft = Offset(size.width.div(2) - s.width.div(2), 4.dp.value),
                    size = s
                )
            }) {
        if (state.isScrollInProgress && !state.canScrollBackward) {
            LottieAnimation(
                composition = composition3,
                progress = { lottieAnimatable.value },
                modifier = Modifier
                    .fillMaxWidth()
                    .height(
                        offsetY
                            .div(24).dp
                    )
            )
        }
        LazyColumn(
            state = state,
            verticalArrangement = Arrangement.spacedBy(8.dp),
            contentPadding = PaddingValues(12.dp)
        ) {
            items(30) {
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(50.dp),
                    colors = CardDefaults.cardColors(containerColor = Color(0xFFbdc9ce))
                ) {}
            }

        }
    }
}
相关推荐
平头哥技术团队8 小时前
Day 10 | 工欲善其事:VS Code 配置与项目归档
android·开发语言·前端·javascript·html·交互
冬木家居9 小时前
40㎡客厅变形记,小家住出大自由[特殊字符]
android·经验分享·笔记·智能家居·微信公众平台
AFinalStone10 小时前
Android 7系统无障碍服务(六)输入事件拦截与 TouchExplorer
android·无障碍服务
天空之城--10 小时前
MT管理器Android逆向工程完全指南:从入门到实战
android
Anhty11 小时前
2026九月最新变声器测评:iOS安卓双端适配,低延迟运行更稳定
android·人工智能·功能测试·ios·智能手机
渡我白衣13 小时前
并查集:基础认识与模拟实现
android·java·javascript·数据结构·c++·算法·并查集
Android-Flutter14 小时前
flutter GetX 详解
android·flutter
MyFreeIT16 小时前
Android Manual
android
hai_android17 小时前
AIDL 实现 IPC 进程间通信
android·android studio
AFinalStone18 小时前
Android 7系统无障碍服务(五)AccessibilityNodeInfo 的构建与查询
android·无障碍服务