Jetpack Compose花里胡哨 - 40+行实现左右侧滑返回、带动画、有细节的Activity

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

此处代码仅作为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,如有侵权,请联系删除。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容