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))
                ) {}
            }

        }
    }
}
相关推荐
2603_949462105 小时前
Flutter for OpenHarmony社团管理App实战:意见反馈实现
android·flutter
错把套路当深情5 小时前
android两种渠道支持一键打包 + 随意组合各种渠道
android
彬sir哥6 小时前
android studio如何把.gradle从C盘移到D盘
android·gradle·maven·android studio
、BeYourself8 小时前
TabLayout 与 ViewPager2 的基本使用
android·android-studio
南村群童欺我老无力.8 小时前
Flutter 框架跨平台鸿蒙开发 - 城市文创打卡:探索城市文化创意之旅
android·flutter·华为·harmonyos
Madison-No78 小时前
【Linux】文件操作&&重定向原理
android·linux·运维
2603_949462109 小时前
Flutter for OpenHarmony社团管理App实战:消息中心实现
android·javascript·flutter
andr_gale9 小时前
08_flutter中如何优雅的提前获取child的宽高
android·flutter
踏雪羽翼10 小时前
android 图表实现
android·折线图·弹窗·图表·自定义图标
有位神秘人10 小时前
Android中PopupWindow中如何弹出时让背景变暗
android