Activity侧滑返回的实现原理
简介
使用侧滑Activity返回很常见,例如微信就用到了。那么它是怎么实现的呢。本文带你剖析一下实现原理。我在github上找了一个star有2.6k的开源,我们分析他是怎么实现的
//star 2.6k
'com.r0adkll:slidableactivity:2.0.5'
Slidr使用示例
它的使用很简单,首先要设置透明的窗口背景
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:textAllCaps">false</item>
<item name="android:windowActionBar">false</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
然后
//setContent(View view)后
Slidr.attach(this);

下面可以从三个步骤看其原理
步骤一 重新包裹界面
Slidr.class
public static SlidrInterface attach(final Activity activity, final int statusBarColor1, final int statusBarColor2){
//0 创建滑动嵌套界面SliderPanel
final SliderPanel panel = initSliderPanel(activity, null);
//7 Set the panel slide listener for when it becomes closed or opened
// 监听回调
panel.setOnPanelSlideListener(new SliderPanel.OnPanelSlideListener() {
...
//open close等
});
// Return the lock interface
return initInterface(panel);
}
private static SliderPanel initSliderPanel(final Activity activity, final SlidrConfig config) {
//3 获取decorview
ViewGroup decorView = (ViewGroup)activity.getWindow().getDecorView();
//4 获取我们布局的内容并删除
View oldScreen = decorView.getChildAt(0);
decorView.removeViewAt(0);
//5 Setup the slider panel and attach it to the decor
// 建立滑动嵌套视图SliderPanel并且添加到DecorView中
SliderPanel panel = new SliderPanel(activity, oldScreen, config);
panel.setId(R.id.slidable_panel);
oldScreen.setId(R.id.slidable_content);
//6 把我们的界面布局添加到SliderPanel,并且把SliderPanel添加到decorView中
panel.addView(oldScreen);
decorView.addView(panel, 0);
return panel;
}
步骤二 使用ViewDragHelper.class处理滑动手势
SliderPanel.class
private void init(){
...
//1 ViewDragHelper创建
mDragHelper = ViewDragHelper.create(this, mConfig.getSensitivity(), callback);
mDragHelper.setMinVelocity(minVel);
mDragHelper.setEdgeTrackingEnabled(mEdgePosition);
//2 Setup the dimmer view 添加用于指示滑动过程的View到底层
mDimView = new View(getContext());
mDimView.setBackgroundColor(mConfig.getScrimColor());
mDimView.setAlpha(mConfig.getScrimStartAlpha());
addView(mDimView);
}
步骤三 在ViewDragHelper.Callback中处理我们的界面的拖动
我们首先明确ViewDragHelper仅仅是处理ParentView与它子View的关系,不会一直遍历到最顶层的View。ViewDragHelper的捕获capture是这样实现的
@Nullable
public View findTopChildUnder(int x, int y) {
final int childCount = mParentView.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
if (x >= child.getLeft() && x < child.getRight()
&& y >= child.getTop() && y < child.getBottom()) {
return child;
}
}
return null;
}
重点在SliderPanel.class的ViewDragHelper.Callback callback的实现,作者实现实现了很多个方向的滑动处理mLeftCallback、mRightCallback、mTopCallback、mBottomCallback、mVerticalCallback、mHorizontalCallback, 我们取mLeftCallback来分析
private ViewDragHelper.Callback mLeftCallback = new ViewDragHelper.Callback() {
//捕获View
@Override
public boolean tryCaptureView(View child, int pointerId) {
boolean edgeCase = !mConfig.isEdgeOnly() || mDragHelper.isEdgeTouched(mEdgePosition, pointerId);
//像前面说的,我们的内容是最上层子View,mDecorView这里指的是我们的contentView
return child.getId() == mDecorView.getId() && edgeCase;
}
//拖动, 最终是通过view.offsetLeftAndRight(offset)实现移动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return clamp(left, 0, mScreenWidth);
}
//滑动范围
@Override
public int getViewHorizontalDragRange(View child) {
return mScreenWidth;
}
//释放处理,判断是滚回屏幕
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
int left = releasedChild.getLeft();
int settleLeft = 0;
int leftThreshold = (int) (getWidth() * mConfig.getDistanceThreshold());
boolean isVerticalSwiping = Math.abs(yvel) > mConfig.getVelocityThreshold();
if(xvel > 0){
if(Math.abs(xvel) > mConfig.getVelocityThreshold() && !isVerticalSwiping){
settleLeft = mScreenWidth;
}else if(left > leftThreshold){
settleLeft = mScreenWidth;
}
}else if(xvel == 0){
if(left > leftThreshold){
settleLeft = mScreenWidth;
}
}
//滚动到left=0(正常布局) 或者 滚动到left=mScreenWidth(滚出屏幕)关闭Activity
mDragHelper.settleCapturedViewAt(settleLeft, releasedChild.getTop());
invalidate();
}
//转换位置百分比,确定指示层的透明度
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
float percent = 1f - ((float)left / (float)mScreenWidth);
if(mListener != null) mListener.onSlideChange(percent);
// Update the dimmer alpha
applyScrim(percent);
}
//回调到Slidr处理Activity状态
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
if(mListener != null) mListener.onStateChanged(state);
switch (state){
case ViewDragHelper.STATE_IDLE:
if(mDecorView.getLeft() == 0){
// State Open
if(mListener != null) mListener.onOpened();
}else{
// State Closed 这里回调到Slidr处理activity.finish()
if(mListener != null) mListener.onClosed();
}
break;
case ViewDragHelper.STATE_DRAGGING:
break;
case ViewDragHelper.STATE_SETTLING:
break;
}
}
};
对于mDragHelper.settleCapturedViewAt(settleLeft, releasedChild.getTop());内部是使用Scroller.class辅助滚动,所以要在SliderPanel中重写View.computeScroll()
@Override
public void computeScroll() {
super.computeScroll();
if(mDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
总结
整体方案如下图所示

总体来看原理并不复杂, 就是通过ViewDragHelper对View进行拖动。
Activity侧滑返回的实现原理的更多相关文章
- 全新的手势,侧滑返回、全局右滑返回都OUT啦!
前言 Android快速开发框架-ZBLibrary 最近将以前的 全局右滑返回 手势功能改成了 底部左右滑动手势. 为什么呢?为了解决滑动返回手势的问题. 目前有3种滑动返回手势 一.侧滑返回 代表 ...
- Android中Activity处理返回结果的实现方式
大家在网上购物时都有这样一个体验,在确认订单选择收货人以及地址时,会跳转页面到我们存入网站内的所有收货信息(包含收货地址,收货人)的界面供我们选择,一旦我们点击其中某一条信息,则会自动跳转到订单提交界 ...
- Activity详解三 启动activity并返回结果
首先看演示: 1 简介 .如果想在Activity中得到新打开Activity 关闭后返回的数据,需要使用系统提供的startActivityForResult(Intent intent, int ...
- 一行代码,让你的应用中UIScrollView的滑动与侧滑返回并存
侧滑返回是iOS系统的一个很贴心的功能,特别是在大屏手机上,单手操作的时候去按左上角的返回键特别不方便.当我在使用一个APP的时候,如果控制器不能侧滑返回,我会觉得这个APP十分不友好...这款产品在 ...
- 【Android 复习】:从Activity中返回数据
在实际的应用中,我们不仅仅要向Activity传递数据,而且要从Activity中返回数据,虽然返回数据和传递类似,也可以采用上一讲中的四种方式来传递数据,但是一般建议采用Intent对象的方式的来返 ...
- Android_打开多个Activity,返回到第一个Activity
正文 一.流程截图 二.问题说明 依次从登录到三级界面,然后退出回到登录界面. 三.解决办法 3.1 实现代码 三级界面调用如下代码: Intent intent = new Inte ...
- 安卓activity捕获返回button关闭应用的方法
安卓activity捕获返回button关闭应用的方法 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { //按下键盘 ...
- Android - 和其他APP交互 - 获得activity的返回值
启用另一个activity不一定是单向的.也可以启用另一个activity并且获得返回值.要获得返回值的话,调用startActivityForResult()(而不是startActivity()) ...
- iOS7以后的侧滑返回上一页
我们知道,iOS7以后,导航控制器默认带了侧滑返回功能,但是仅限于屏幕边缘.而且在你自定义leftBarButtonItem等之后侧滑效果就会消失.这种问题怎么解决呢? 首先,我们先来看看系统的这种手 ...
随机推荐
- mysqldump中skip-tz-utc参数介绍
前言: 在前面文章中,有提到过 mysqldump 备份文件中记录的时间戳数据都是以 UTC 时区为基础的,在筛选恢复单库或单表时要注意时区差别.后来再次查看文档,发现 tz-utc.skip-tz- ...
- [技术博客]iview组件样式踩坑记录
[技术博客]iview组件样式踩坑记录 iview官方文档. 在本次项目开发中,前端项目主要使用vue框架+iview组件构建,其中iview组件在使用过程中遇到了许多官方文档中没有明确说明或是很难注 ...
- CSS中margin负值巧布局
margin负值实现细边框 我们先准备五个div盒子,并设置好浮动和2px的实线黑色边框,看看效果 中间的边框线挨在了一起致使边框变粗成了4px,这时使用margin负值就可以解决这个问题 <s ...
- xxl-job的一些感悟与规范
后台计划任务设计思路: 日志埋点处理,便于prd排查问题 2种主动job搭配规范(正向job.反查job) 1种消息接收的处理规范,重试机制,返回状态 job开关维度 数据流图 线上暗job-便捷性- ...
- MSSQL·备份数据库中的单表
阅文时长 | 0.11分钟 字数统计 | 237.6字符 主要内容 | 1.引言&背景 2.声明与参考资料 『MSSQL·备份数据库中的单表』 编写人 | SCscHero 编写时间 | 20 ...
- [Qt] 事件机制(三)
在主窗口Widget中增加几个小功能 1.点击左键,在左上角label中显示"haha",点击右键,显示"lala" 在widget.h中添加: 1 #incl ...
- Linux shell sed命令在文件行首行尾添加字符
昨天写一个脚本花了一天的2/3的时间,而且大部分时间都耗在了sed命令上,今天不总结一下都对不起昨天流逝的时间啊~~~ 用sed命令在行首或行尾添加字符的命令有以下几种: 假设处理的文本为test.f ...
- SSH连接自动断开的解决方法(deb/rpm)
######### 修改后的: ## # tail -f -n 20 sshd_config#MaxStartups 10:30:60#Banner /etc/issue.net # Allow cl ...
- 列出 Ubuntu 和 Debian 上已安装的软件包
列出 Ubuntu 和 Debian 上已安装的软件包 如果你经常用 apt 命令,你可能觉得会有个命令像 apt 一样可以列出已安装的软件包.不算全错. apt-get 命令 没有类似列出已安装软件 ...
- IDEA 2019.2.4 破解安装教程
将下载的 IDEA 压缩包解压,找到 idealIU-2019.2.4.exe 安装文件,然后双击进行安装 安装完后不要运行,打开解压包中破解补丁与激活码文件夹,找到 jetbrains-agent. ...