18 年底做 Readhub APP 时就加入了这个返回动画效果。一直到现在,才有时间来简单总结和封装一下。
不知道这个返回手势动画到底是 MIUI 还是「即刻」APP 首创,因为我那会儿还没用上 MIUI 全面屏。不过 MIUI 全面屏那个返回手势相对简单,固定位置。从这个效果上看,我觉得是 MIUI 先有这个效果,然后 「即刻」APP 优化丰富了一下。当然,可能还有另外一种情况,这是早就有的效果图,只是在目前,我已知有使用的就上面两个场景。
总结起来很简单,就是一个「贝塞尔曲线」的绘制,再外加一个箭头绘制。箭头什么的的绘制在之前的仓库中已经练习过很多。这次着重说说这个特殊图形需要怎么绘制。
对于贝塞尔曲线绘制,之前玩过两阶、三阶的。第一次看到这个效果,觉得贝塞尔曲能实现,但是这是几阶的,高阶的怎么玩,那就是从头开始。
在经过一番把玩探索后,最终确定这就是五阶的贝塞尔曲线, Android
似乎默认没有高阶对应的 api。
那这怎么办呢?曲线就是函数嘛,肯定有公式,网上就搜到公式写法。
private fun calculateY(i: Int, j: Int, t: Float): Float {
return if (i == 1) {
(1 - t) * controlPoints[j].y + t * controlPoints[j + 1].y
} else (1 - t) * calculateY(i - 1, j, t) + t * calculateY(i - 1, j + 1, t)
}
复制代码
controlPoints 对应的就是那个五个控制点的集合。
addControlPoint(0, 0f, yResult - maxPeakValue * 1.5f)
addControlPoint(1, 0f, yResult - maxPeakValue * 1.5f * GOLDEN_RATIO)
addControlPoint(2, min, yResult)
addControlPoint(3, 0f, yResult + maxPeakValue * 1.5f * GOLDEN_RATIO)
addControlPoint(4, 0f, yResult + maxPeakValue * 1.5f)
复制代码
接着就是控制是否拦截事件,我现在是做成 Helper 这种工具类型,和对应的 ViewGroup
是解耦的,其实就是 ViewDragHelper
的一个简单实现。
对了,最后考虑下来,实现了左右两边的滑动效果。至于上下,我觉得这种场景不大,就懒得去做了。
具体代码细节就不贴了,源码也没多少,这里讲遇到的一些细节问题或者写出来的 bug
返回退出应用后,后台程序预览中存在返回手势效果
这里其实就是一个先后问题,一开始是同步执行 invalidate()
和 onBackReleased()
。后来使用 Runnable
+ postDalay()
来延迟 onBackReleased()
执行。
贝塞尔曲线贴近屏幕的地方总有一个像素的白线
这里后面排查出来是我添加控制点,for 循环时角标是[0,length-1],最后一个没有添加计算到,所以计算出来的控制点就少了一个。最终效果就是绘制出来的图形 x 轴没有完全对称。偏差就在 1px 左右。 最后的效果就是总感觉下方有一跟白线。
for 循环处理好之后,发现右边绘制出来还是会有这个情况,具体原因不清楚,因为单看数据层面,它肯定是贴边的。最后很讨巧,使用到 translate()
将 Canvas
平移一个像素规避掉,这简直是程序员的小巧思,哈哈 ?。
控制点数量及缓存
因为是用公式算的控制点,所以每一次绘制,其实简单理解就是讲一个一个点连接成一条曲线的。那么问题就是,点多,曲线当然最逼真,但是单位时间处理的数据量就上去了。 点少,可能你看到就是折线图了。 最后均衡在 50 个控制点,点与点之间的比例就是 2% 。在绘制等方法中肯定不能频繁创建对象,所以这 50 个点需要复用。