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

        }
    }
}
相关推荐
婵鸣空啼1 小时前
GD图像处理与SESSiON
android
sunly_2 小时前
Flutter:导航固定背景图,滚动时导航颜色渐变
android·javascript·flutter
用户2018792831673 小时前
简单了解android.permission.MEDIA_CONTENT_CONTROL权限
android
_一条咸鱼_3 小时前
Android Runtime类卸载条件与资源回收策略(29)
android·面试·android jetpack
顾林海3 小时前
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
android·面试·性能优化
砖厂小工3 小时前
Now In Android 精讲 8 - Gradle build-logic 现代构建逻辑组织方式
android
玲小珑3 小时前
Auto.js 入门指南(八)高级控件与 UI 自动化
android·前端
harry235day3 小时前
Compose 带动画的待办清单列表页
android·android jetpack
vocal3 小时前
我的安卓第一课:四大组件之一Activity及其组件RecyclerView
android
咕噜企业签名分发-淼淼3 小时前
如何实现安卓端与苹果端互通的多种方案
android