美文网首页
Jetpack Compose花里胡哨 - 40+行实现左右侧滑

Jetpack Compose花里胡哨 - 40+行实现左右侧滑

作者: 安安_660c | 来源:发表于2023-01-16 14:07 被阅读0次

    参数还需要调优,速度计算也只是不考虑最终抬手速度的粗略版本

    此处代码仅作为demo,实际上线还需要优化参数和速度计算逻辑

    功能简述

    • 左右侧滑都可以让Activity返回
    • 滑动带动Activity时,可看到其下面的内容
    • 滑动不超过指定范围取消此次滑动返回
    • 滑动超过某范围后松手,则Activity自动滑出
    • 滑动速度过大时,即便滑动范围不够,也让Activity自动滑出
    • 滑动被取消时,Activity再滑回原位

    先看最终效果

    image.png

    原理

    说来简单,对设置好window透明的Activity的decorView做跟随手势的x轴偏移,再加一点点动画效果和速度计算即可


    跟着我,一步步来~

    实现一个透明的Activity

    我们需要它被滑动的时候能看到底下的情况,设置其主题即可

    <style name="Theme.diy" parent="Theme.TestApp">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
    </style> 
    
    

    设置拖曳监听

    此处需求,用Modifier.draggable()就够了。

    Modifier.draggable(
            rememberDraggableState { delta ->
                //+=delta即可得到最终滑动值
            },
            Orientation.Horizontal,//横向滑动
            onDragStarted = {
               //在滑动开始时监听
            },
            onDragStopped = {
                //滑动结束时计算最终速度、并决定是让Activity位置重置还是退出
            }
    )
    
    

    最终代码

    思路一目了然了,直接上代码分析一波——

    假定以下内容发生在Activity的setContent{}行为内部,

    所以有一个this@Activity去获得当前Activity

    val screenXMax = remember { Resources.getSystem().displayMetrics.widthPixels.toFloat() }//屏幕宽px,转成float方便后续使用
    val closePerc = remember { 0.55f }//百分比
    val maxSpeed = remember { 4.5f }//最大速度,超过这个速度的滑动会导致Activity无视滑动距离直接关闭
    var speed by remember { mutableStateOf(0f) }//计算出的速度
    
    var targetOffsetX by remember { mutableStateOf(0f) }//Activity的最终偏移目标
    val activityOffset = remember { Animatable(0f) }//Activity的偏移,动画
    
    var dragging by remember { mutableStateOf(false) }//是否正在被滑动
    var timeToClose by remember { mutableStateOf(false) }//是时候关闭了?
    
    //监听最终目标的改变和滑动状态的改变
    LaunchedEffect(targetOffsetX, dragging) {
        //当已经判定为“是时候关闭Activity了”
        if (timeToClose) {
            activityOffset.animateTo(
            //最终目标为正,右滑出去,否则左滑出去
            if (targetOffsetX > 0) screenXMax else -screenXMax, getTween(300, easing = LinearOutSlowInEasing)) {
                //动画的每一帧都让decorView的偏移和动画的值对齐,实现decorView的动画
                this@Activity.window.decorView.x = this.value
            }
            //关闭Activity
            finish()
        } else if (dragging) {
            //滑动过程中让Activity偏移和目标值实时对齐
            activityOffset.snapTo(targetOffsetX)
            this@Activity.window.decorView.x = targetOffsetX
        } else activityOffset.animateTo(targetOffsetX, getTween(300, easing = FastOutLinearInEasing)) {
            //进入这一块意味着已经用户松手了,让decorView动画到Activity的目标值(此时targetOffsetX=0)
            this@Activity.window.decorView.x = this.value
        }
    }
    Box(
            Modifier.draggable(
                    rememberDraggableState { delta ->
                        //跟踪滑动偏移
                        targetOffsetX += delta
                    },
                    Orientation.Horizontal,//横向滑动
                    onDragStarted = {
                        dragging = true
                        Timer.setTag("closeAct")//这是我自定义的一个Timer函数,它会把当前时间戳记在传入的tag中
                    },
                    onDragStopped = {
                        dragging = false
                        //速度等于路程除时间
                        speed = abs(targeOffsetX) / Timer.getTimeInterval("closeAct")//自定义的一个Timer函数,它会返回当前时间与上次调用Timer.setTag(tag)的时间差
                        if (abs(targeOffsetX) > closePerc * screenXMax || speed > maxSpeed) {
                            timeToClose = true
                            return@draggable
                        } else {
                            timeToClose = false
                        }
                        targeOffsetX = 0f
                    }
            )
                .background(Color.Red)//给个红色背景好区分此Activity
                .fillMaxSize()//这个box没内容,于是让它用红色占满整个Activity
    )
    
    

    可改进点

    很显而易见的,速度计算采用的时间是从滑动开始到结束的时间,这不合理。

    速度计算采用的路程是像素,这也不合理。

    应当指定为:滑动的最后50ms所经过的物理距离(如:多少英寸)。

    这个100ms也是我随便说的,但是应该越小越好,**且需要考虑到时间戳在不同设备上可能有不同的生成间隔,所以不能太小**,只有这样才能尽可能精确地计算出用户最后抬手瞬间的速度

    另外scroll动作的惯性是怎么实现的,计算速度这里或许可以参考下,我没研究,我不知道,哈哈哈

    说到这里你应该有关于速度的优化方案了,我就懒得进一步完善了。hhh

    本文转自 https://juejin.cn/post/7063383771088683045,如有侵权,请联系删除。

    相关文章

      网友评论

          本文标题:Jetpack Compose花里胡哨 - 40+行实现左右侧滑

          本文链接:https://www.haomeiwen.com/subject/repzcdtx.html