code小生,一个专注 Android 领域的技术分享平台
作者:Android_YangKe
地址:https://www.jianshu.com/p/d62cda41017c
声明:本文已获 Android_YangKe 授权,转发等请联系原作者授权
微信是腾讯家族的一款旗舰产品,前些日子实在无聊就可劲刷朋友圈,刹那间发现微信具有二级页面滑动关闭功能,(屏随指动,纵享丝滑)用户好感倍增,顿时两眼泛水花开始膜拜大厂产品,工程师。
努力成为自己想要成为的人,心中默念:“当你停下来休息的时候,不要忘记别人还在奔跑”。于是开始学习相关技术丰富自己。百度,Google,Github,功夫不负有心人,通过自己几个小时的努力实现了自己想要的效果。我们看图:
Android_YangKe.gif Android_YangKe.gif由于动态图压缩比较严重,这里进行简单讲解。图一效果主要是:当我们手指没有将页面拖动到屏幕中间及后半部分时,应用会自动回弹到原始状态,当我们将页面拖动到屏幕后半部分时页面会 finish 掉。功能实现了,具体思路是什么呢?如果你对Activity,Window,View 整体轮廓不是很熟悉,请做知识储备 点我。
下面我们看下项目结构:
Android_YangKe.png不要重复造轮子,没错这是一个三方库。但我们争取做到知其然,知其所以然。下面开始我们的分析,注意其中这样几个 API:
SwipeBackActivity
SwipeBackActivityHelper
SwipeBackActivityBase
SwipeBackLayout
ViewDragHelper
SwipeBackActivityBase
SwipeBackActivityBase从命名上我们可以看出此类为基类,它是滑动关闭 Activity 的抽象。为什么选择基类入手?一般阅读源码代码量都比较大,从基类入手便于我们了解整体框架轮廓,至于实现细节,我们根据需要再来分析。
代码之旅,即将开始:3,2,1…
public interface SwipeBackActivityBase {
/**
* 从当前 Activity 返回关联的 SwipeBackLayout 对象
*/
public abstract SwipeBackLayout getSwipeBackLayout();
/**
* 设置开启或者关闭滑动关闭 Activity 功能
*/
public abstract void setSwipeBackEnable(boolean enable);
/**
* 滑动 Activity 关闭 Activity 函数的抽象
*/
public abstract void scrollToFinishActivity();
}
从以上代码我们可以 get 到三个技能点:
1> 获取当前 Activity 关联的 SwipeBackLayout 对象
2> 开启或者关闭滑动 Activity 关闭 Activity 的功能
3> 滑动关闭 Activity
SwipeBackActivity
SwipeBackActivity是SwipeBackActivityBase的具体实现。也就是说我们的 Activity 继承此类后相应的页面就具有了滑动关闭功能。我们继续,前方有大量源码即将来袭,请注意查收!
public class SwipeBackActivity extends AppCompatActivity implements SwipeBackActivityBase {
private SwipeBackActivityHelper mHelper;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHelper = new SwipeBackActivityHelper(this);
mHelper.onActivityCreate();
}
@Override protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mHelper.onPostCreate();
}
@Override public SwipeBackLayout getSwipeBackLayout() {
return mHelper.getSwipeBackLayout();
}
@Override public void setSwipeBackEnable(boolean enable) {
getSwipeBackLayout().setEnableGesture(enable);
}
@Override public void scrollToFinishActivity() {
Utils.convertActivityToTranslucent(this);
getSwipeBackLayout().scrollToFinishActivity();
}
}
代码是不是看起来很简洁,但简洁并不代表简单。我们可以看到SwipeBackActivity实现了SwipeBackActivityBase并重写了相应函数。同时,用户的滑动方向,手势判断,滑动阴影一并完成。良好的设计模式在这里体现的淋漓尽致,内功很重要,扯远了哈。通过以上代码我们可以获取到以下信息:
1> 首次出现的 SwipeBackActivityHelper
2> 我通过 SwipeBackActivityHelper 对象获取 SwipeBackLayout 对象
3> scrollToFinishActivity 函数的实现
不知道大家有没有发现一个共同点就是,所有的操作都必须依托于SwipeBackLayout对象,而SwipeBackLayout对象又依托于SwipeBackActivityHelper,好吧,我们来看下SwipeBackActivityHelper。
当我翻开源码的时候其实内心是拒绝的,作者竟然没有给出一行注释。不过好在代码量不大,硬着头皮读吧。
public class SwipeBackActivityHelper {
private Activity mActivity;
private SwipeBackLayout mSwipeBackLayout;
public SwipeBackActivityHelper(Activity activity) { this.mActivity = activity; }
/**
* 1> 处理 Activity 关联的 Window 背景
* 2> 处理 Window 关联的 DecorView 对象
* 3> 初始化 SwipeBackLayout 对象
*/
public void onActivityCreate() {
mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mActivity.getWindow().getDecorView().setBackgroundDrawable(null);
mSwipeBackLayout = (SwipeBackLayout) LayoutInflater.from(mActivity).inflate(
me.imid.swipebacklayout.lib.R.layout.swipeback_layout, null);
}
public void onPostCreate() {
mSwipeBackLayout.attachToActivity(mActivity);
}
public SwipeBackLayout getSwipeBackLayout() {
return mSwipeBackLayout;
}
}
onActivityCreate文中已经给出了注释,这里不再进行说明。代码掐头去尾就剩下onPostCreate函数,现在让我们找到SwipeBackLayout.java,然后定位到attachToActivity函数。
//SwipeBackLayout.java
public void attachToActivity(Activity activity) {
...代码省略...
mActivity = activity;
ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
decorChild.setBackgroundResource(background);
decor.removeView(decorChild);
addView(decorChild);
setContentView(decorChild);
addSwipeListener(new SwipeBackListenerActivityAdapter(activity));
decor.addView(this);
}
通过对上文的分析我们可以获取到以下几点信息:
1> 从当前 Activity 中获取 DecorView。DecorView 与 Activity 的恩怨情仇
2> 将手势监听与当前 Activity 绑定
3> 将 SwipeBackLayout 添加到 DecorView,也可以理解为将视图依托在 Activity 上进行展示。
SwipeBackLayout
public class SwipeBackLayout extends FrameLayout {
...代码省略百行...
}
对于自定义 View、ViewGroup 来说,一般代码量都比较大,动则成百上千,那么我们该使用什么样的姿势来打开它呢。其实从自定义 View 的几个关键函数开始就行,当然还有就是构造函数。
public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {
...代码省略...
mDragHelper = ViewDragHelper.create(this, new ViewDragCallback());//构建ViewDragHelper
int mode = EDGE_FLAGS[a.getInt(R.styleable.SwipeBackLayout_edge_flag, 0)];
setEdgeTrackingEnabled(mode);//设置滑动关闭 Activity 的模式。例:左侧关闭,右侧关闭等。
int shadowLeft = a.getResourceId(R.styleable.SwipeBackLayout_shadow_left, R.drawable.shadow_left);
int shadowRight = a.getResourceId(R.styleable.SwipeBackLayout_shadow_right, R.drawable.shadow_right);
int shadowBottom = a.getResourceId(R.styleable.SwipeBackLayout_shadow_bottom, R.drawable.shadow_bottom);
setShadow(shadowLeft, EDGE_LEFT);//设置左侧滑动时的阴影
setShadow(shadowRight, EDGE_RIGHT);//设置右侧滑动时的阴影
setShadow(shadowBottom, EDGE_BOTTOM);//设置底部滑动时的阴影
}
经过分析构造函数中大概做了如下几件事情:
1> 初始化属性信息
2> 构建 ViewDragHelper 对象
3> 初始化页面滚动时左、右、下侧阴影部分
然后就是 set 函数:
1> setEdgeTrackingEnabled //设置滑动方向。例:左侧滑动,右侧滑动
2> setShadow //设置阴影方向。例:左侧,右侧
3> setSwipeBackEnable // 设置是否开启滑动关闭功能,默认为 true
… 等等
如何给自己的应用添加滑动关闭 Activity 功能?
将自己 Activity 继承自 SwipeBackActivity
如何开启或关闭“滑动关闭功能”?
调用 setSwipeBackEnable(false); 可以禁止滑动关闭功能
如何修改滑动关闭 Activity 的方向?
setEdgeTrackingEnabled(SwipeBackLayout.EDGE_XXX);
对于 SwipeBackLayout 的理解:
1> 将依附在 Activity 上的 Window、DecorView 背景设置为空,透明
2> 将 DecorView 替换为 SwipeBackLayout
3> 对 SwipeBackLayout 进行手势监听,处理滑动事件动态更新页面
APK 下载
http://yangwenxue.gitee.io/test/personfile/search-1.0-2018-06-19-release.apk