Github 上滑动返回库比较多,由于实现思路甚至代码一部分库都差不多,所以只挑选了两个实现思路比较不同的库作为研究,分别是 SwipeBackLayout 和 and_swipeback。
除滑动返回功能外,本文还会围绕滑动过程中呈现前一个界面的方案对上述两个库展开分析,其他部分源码细节不予分析。
SwipeBackLayout
由于使用 SwipeBackLayout 库提供的滑动返回能力需要继承于 SwipeBackActivity
,所以我们直接从 SwipeBackActivity
开始研究。
|
|
可以看出 SwipeBackActivity
实际上只是 SwipeBackActivityHelper
的代理。
|
|
从 SwipeBackActivityHelper
的代码不难看出它主要的工作是在 Activity 创建的时候将 Activity 的背景修改为透明以及创建 SwipeBackLayout
并将其与 Activity 关联。再来看一下 mSwipeBackLayout.attachToActivity(mActivity)
的逻辑:
|
|
SwipeBackLayout
首先通过 Activity 实例获取当前主题下 windowBackground
的色值,并将其设置为 ContentView 的背景色,然后将 ContentView 添加至 SwipeBackLayout
中并将 SwipeBackLayout
设为当前 Activity 的 ContentView。至此便完成了与 Activity 关联的工作。
滑动处理
开始滑动时,SwipeBackLayout
将滑动事件全部转发至 ViewDragHelper
处理,等待 SwipeBackLayout.ViewDragCallback
中接收到相应回调后执行相关逻辑。
|
|
当 ViewDragHelper
将当前状态修改为 STATE_DRAGGING
后,ViewDragHelper
通过调用 ViewDragCallback
的 clampViewPositionHorizontal
或 clampViewPositionVertical
回调方法设定当前页面的 ContentView 应移动的距离。
|
|
当滑动事件结束后(onViewReleased
),SwipeBackLayout
通过判断此前在 onViewPositionChanged
中计算好的滑动率是否达到阈值以执行回弹或退出动画,这样就完成了一次完整的滑动过程。
|
|
显示前一个页面
为了显示前一个 Activity 界面,SwipeBackActivityHelper
在 Activity onCreate 时就将当前 Window 设置为透明背景。
|
|
而单单设置为透明背景并不足以显示上一个 Activity。为了解决这个问题,SwipeBackActivityHelper
在接收到 SwipeListener
的 onEdgeTouch
回调后调用 Utils.convertActivityToTranslucent(mActivity)
来让前一个 Activity 显示出来。
|
|
其中 onEdgeTouch
是在 ViewDragCallback
的 tryCaptureView
回调中被调用的。
|
|
那么为什么 Utils.convertActivityToTranslucent(mActivity)
可以让前一个 Activity 显示呢?我们来看一下相关的逻辑:
|
|
其中反射调用的 convertToTranslucent
方法的 Android 官方注释中写到 “Calling this allows the Activity behind this one to be seen again” 。所以,由于 convertActivityToTranslucent
通过反射调用 Activity 的 convertToTranslucent
方法将 Activity 转为 Translucent(相当于 windowIsTranslucent 值为 true),配合之前的透明背景,那么前一个 Activity 也就能显示出来了。
以上即为 SwipeBackLayout
库为了显示上一界面所做的操作,然而这种方法有其弊端。由于被设为透明背景的 Activity 的前一个 Activity 无法进入 onStop(),从而导致 Activity 的 Window 不能释放部分资源。一旦多个透明背景的 Activity 叠加会出现明显的卡顿现象(毕竟这么多 View 需要绘制)。
and_swipeback
and_swipeback 与 SwipeBackLayout 一样提供了自定义的 Activity 让用户直接继承以集成滑动返回功能。
|
|
可以看到 SwipeBackActivity
先将触摸事件转发给 SwipeBackHelper
处理,如未被消费再交由自身处理。
滑动处理
在触摸事件处理上 and_swipeback 并没有使用 ViewDragHelper
,而是让 SwipeBackHelper
直接消费触摸事件。
|
|
当 processTouchEvent
判定当前的触摸事件为滑动事件时,SwipeBackHelper
对自身发送了一个 MSG_ACTION_MOVE
的 Message。接收到该 Message 后,SwipeBackHelper
调用了 onSliding
方法以执行滑动动画。
|
|
与 ViewDragHelper
不同的是,SwipeBackHelper
通过调用 View 的 setX
方法来显示位移效果。最后,当手势结束时,SwipeBackHelper
在滑动距离不足时会执行回弹动画,在滑动距离充足时则会一直滑到尽头再结束当前页面,此处逻辑比较简单,不贴出代码。
显示前一个页面
and_swipeback 对前一个界面的管理主要放在 SwipeBackHelper.ViewManager
中,
|
|
当调用 addViewFromPreviousActivity
时,ViewManager
从 ActivityLifecycleHelper
中获得上一个 Activity 的实例(其中 ActivityLifecycleHelper
实现了 Application.ActivityLifecycleCallbacks
并需在 Application 中注册监听,从而在 Activity 栈发生变化时能够获取到每个 Activity 的实例),并将该 Activity 的 ContentView 添加到当前 Activity 的底部。当滑动时,通过移开上层的 View 则可看到底部的 Previous ContentView。
相比 SwipeBackLayout,and_swipeback 避免了过多透明背景页面时所造成的卡顿感。
总结
在滑动处理上,两个库的能力其实都一样,也不存在与滑动控件冲突的情况。在显示前一个页面的方案上,从性能角度自然是 and_swipeback 更好,但实现逻辑上显然更加复杂。
在选择滑动返回库时,如果所需滑动返回的 Activity 叠加数量并不多,使用 SwipeBackLayout 或 and_swipeback 都差不多。而当带滑动返回功能的 Activity 数量较多时,如之前所述,透明背景会造成明显的卡顿感,此时应当使用 and_swipeback。