参数还需要调优,速度计算也只是不考虑最终抬手速度的粗略版本
此处代码仅作为demo,实际上线还需要优化参数和速度计算逻辑
功能简述
- 左右侧滑都可以让Activity返回
- 滑动带动Activity时,可看到其下面的内容
- 滑动不超过指定范围则取消此次滑动返回
- 滑动超过某范围后松手,则Activity自动滑出
- 滑动速度过大时,即便滑动范围不够,也让Activity自动滑出
- 滑动被取消时,Activity再滑回原位
先看最终效果
原理
说来简单,对设置好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,如有侵权,请联系删除。