类似于微信的右滑返回,在BaseActivity里利用dispatchTouchEvent()拦截右滑动作,利用setTranslationX()实现动画,在DecorView里添加View作为滑动时的左侧阴影。
渐进步骤:
注意:步骤中的代码为了不关联到后面的步骤,会与最终的有点不同
背景透明
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item>
activity的跳转动画
根据项目需要,重写用到的startActivity(Intent intent),startActivityForResult(Intent intent, int requestCode),finish()等activity跳转和销毁方法
@Override
public void startActivity(Intent intent) {
super.startActivity(intent);
overridePendingTransition(R.anim.slide_right_in, 0);
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
overridePendingTransition(R.anim.slide_right_in, 0);
}
@Override
public void finish() {
super.finish();
overridePendingTransition(0, R.anim.slide_right_out);
}
//R.anim.slide_right_in
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="100%"
android:toXDelta="0"
android:fromYDelta="0"
android:toYDelta="0"/>
</set>
//R.anim.slide_right_out
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="0"
android:toXDelta="100%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>
拦截右滑动作
所有的触摸事件通过activity.dispatchTouchEvent(MotionEvent ev)向view分发。
手指在X轴方向右滑动50~100px时,判断是否为(产品经理要)右滑动作
private float downX = 0;
private float downY = 0;
private boolean shouldIntercept = false;
private boolean hadJudge = false;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (shouldIntercept) {
return onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
downX = ev.getRawX();
downY = ev.getRawY();
hadJudge = false;
break;
}
case MotionEvent.ACTION_MOVE: {
if (hadJudge) break;
if (ev.getRawX() == downX) break;
if (ev.getRawX() < downX) {
//左滑
hadJudge = true;
break;
}
if (ev.getRawX() - downX >=100){
//超出判断距离
hadJudge = true;
break;
}
if (ev.getRawX() - downX > 50) {
//x轴右滑50~100px
float rate = (ev.getRawX() - downX) / (Math.abs(ev.getRawY() - downY));
if ((downX < 50 && rate > 0.5f) || rate > 2) {
shouldIntercept = true;
}
}
break;
}
case MotionEvent.ACTION_UP: {
downX =0;
downY = 0;
shouldIntercept = false;
hadJudge=false;
break;
}
}
//Activity的默认分发
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return true;
}
根布局位移动画
根据手指滑动距离设置根布局偏移距离,用滑动距离和手指抬起时的速度判断是否返回
private View rootView = null;
private float lastX = -1;
private VelocityTracker velocityTracker = null;
private int maxFlingVelocity;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (rootView == null) {
ViewGroup rootGroup = (ViewGroup) (getWindow().getDecorView());
rootView = rootGroup.getChildAt(0);
}
//测量手指抬起时的速度
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
maxFlingVelocity = ViewConfiguration.get(this).getScaledMaximumFlingVelocity();
}
velocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
lastX = event.getRawX();
break;
}
case MotionEvent.ACTION_MOVE: {
if (lastX == -1) {
lastX = event.getRawX();
break;
}
//根据手指滑动距离设置根布局偏移距离
rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX);
if (rootView.getTranslationX() < 0) rootView.setTranslationX(0);
lastX = event.getRawX();
break;
}
case MotionEvent.ACTION_UP: {
//测量手指抬起时的速度
velocityTracker.computeCurrentVelocity(1000, maxFlingVelocity);
float velocityX = velocityTracker.getXVelocity();
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
//判断是否返回
if (downX < 50 && velocityX > 1000) {
//手指在左侧边落下,返回
onBack();
} else if (velocityX > 3600) {
//手指快速滑动,返回
onBack();
} else if (rootView.getTranslationX() > ConvertUtil.getWidthInPx() * 0.3) {
//滑动距离超过30%屏幕宽度,返回
onBack();
} else {
//不返回,根布局偏移归零
rootView.animate().translationX(0).setDuration(200).start();
}
lastX = -1;
shouldIntercept = false;
hadJudge=false;
downX = 0;
downY = 0;
break;
}
}
return super.onTouchEvent(event);
}
添加左侧阴影
Activity的最顶层View为DecorView,DecorView是一个FrameLayout,里面只有一个Linearlayout,Linearlayout包含着标题栏和自定义布局(setContentView)。
上一步跟随手指滑动进行偏移的就是Linearlayout,现在要在DecorView里添加一个View,设置背景作为阴影,并跟随Linearlayout进行移动
private View shadowView = null;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (rootView == null) {
//添加阴影
ViewGroup rootGroup = (ViewGroup) (getWindow().getDecorView());
shadowView = new View(this);
rootGroup.addView(shadowView, 0);
ViewGroup.LayoutParams params = shadowView.getLayoutParams();
//阴影宽度
params.width = (int) ((float) ConvertUtil.getWidthInPx() * 0.05f);
params.height = ConvertUtil.getHeightInPx();
shadowView.setLayoutParams(params);
shadowView.setBackgroundResource(R.drawable.shadow_grey_h);
shadowView.setTranslationX(params.width);
rootView = rootGroup.getChildAt(1);
}
...
switch (event.getAction()) {
...
case MotionEvent.ACTION_MOVE: {
if (lastX == -1) {
lastX = event.getRawX();
break;
}
//根据手指滑动距离设置根布局偏移距离
rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX);
if (rootView.getTranslationX() < 0) rootView.setTranslationX(0);
//阴影跟随根布局移动
shadowView.setTranslationX(-shadowView.getWidth()+rootView.getTranslationX());
lastX = event.getRawX();
break;
}
case MotionEvent.ACTION_UP: {
...
} else {
//不返回,根布局偏移归零
rootView.animate().translationX(0).setDuration(200).start();
//阴影偏移归零
shadowView.animate().translationX(-shadowView.getWidth()).setDuration(200).start();
}
...
}
}
...
}
关联下层activity滑动
获取下层activity
private static ArrayList<Activity> Activity_Stack = new ArrayList<>();
private BaseSwipeBackActivity lastActivity = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
maxFlingVelocity = ViewConfiguration.get(this).getScaledMaximumFlingVelocity();
if (!Activity_Stack.contains(this)) Activity_Stack.add(this);
if (Activity_Stack.size() >= 2) {
Activity last = Activity_Stack.get(Activity_Stack.size() - 2);
if (last instanceof BaseSwipeBackActivity) {
lastActivity = (BaseSwipeBackActivity) last;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Activity_Stack.remove(this);
}
下层activity的退出、进入动画
private void lowerActivityExitAnim() {
if (rootView == null) return;
//只移动30%
rootView.animate().translationX(-ConvertUtil.getWidthInPx() * 0.3f).setDuration(300).start();
}
private void lowerActivityEnterAnim(float upperTranslationX) {
if (rootView == null) return;
//保证滑动退出时,上下层时间同步
float r = 1-upperTranslationX/ (float) ConvertUtil.getWidthInPx();
rootView.animate().translationX(0).setDuration((long) (300f * r)).start();
}
在跳转时,调用下层activity的退出、进入动画
@Override
public void startActivity(Intent intent) {
super.startActivity(intent);
overridePendingTransition(R.anim.slide_right_in, 0);
lowerActivityExitAnim();
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
overridePendingTransition(R.anim.slide_right_in, 0);
lowerActivityExitAnim();
}
@Override
public void finish() {
super.finish();
overridePendingTransition(0, R.anim.slide_right_out);
if (lastActivity != null) lastActivity.lowerActivityEnterAnim(rootView.getTranslationX());
Activity_Stack.remove(this);
}
上层activity滑动时关联下层滑动
@Override
public boolean onTouchEvent(MotionEvent event) {
...
switch (event.getAction()) {
...
case MotionEvent.ACTION_MOVE: {
...
//根据手指滑动距离设置根布局偏移距离
rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX);
if (rootView.getTranslationX() < 0) rootView.setTranslationX(0);
//阴影跟随根布局移动
shadowView.setTranslationX(-shadowView.getWidth() + rootView.getTranslationX());
//下层activity跟随移动
if (lastActivity != null && lastActivity.rootView != null)
//-ConvertUtil.getWidthInPx() * 0.3f初始的偏移
lastActivity.rootView.setTranslationX(-ConvertUtil.getWidthInPx() * 0.3f + rootView.getTranslationX() * 0.3f);
...
}
case MotionEvent.ACTION_UP: {
...
} else {
//不返回,根布局偏移归零
rootView.animate().translationX(0).setDuration(200).start();
//阴影偏移归零
shadowView.animate().translationX(-shadowView.getWidth()).setDuration(200).start();
//下层activity偏移复原
if (lastActivity != null)
lastActivity.lowerActivityExitAnim();
}
...
}
}
return super.onTouchEvent(event);
}
完整的
public abstract class BaseSwipeBackActivity extends AppCompatActivity {
private static ArrayList<Activity> Activity_Stack = new ArrayList<>();
private BaseSwipeBackActivity lastActivity = null;
private View rootView = null;
private View shadowView = null;
private float downX = 0;
private float downY = 0;
private boolean shouldIntercept = false;
private boolean hadJudge = false;
private float lastX = -1;
private VelocityTracker velocityTracker = null;
private int maxFlingVelocity;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
maxFlingVelocity = ViewConfiguration.get(this).getScaledMaximumFlingVelocity();
if (!Activity_Stack.contains(this)) Activity_Stack.add(this);
if (Activity_Stack.size() >= 2) {
Activity last = Activity_Stack.get(Activity_Stack.size() - 2);
if (last instanceof BaseSwipeBackActivity) {
lastActivity = (BaseSwipeBackActivity) last;
}
}
}
@Override
protected void onResume() {
initShadow();
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
Activity_Stack.remove(this);
}
@Override
public void startActivity(Intent intent) {
super.startActivity(intent);
overridePendingTransition(R.anim.slide_right_in, 0);
lowerActivityExitAnim();
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
overridePendingTransition(R.anim.slide_right_in, 0);
lowerActivityExitAnim();
}
@Override
public void finish() {
super.finish();
overridePendingTransition(0, R.anim.slide_right_out);
if (lastActivity != null) lastActivity.lowerActivityEnterAnim(rootView.getTranslationX());
Activity_Stack.remove(this);
}
private void initShadow() {
if (shadowView == null) {
ViewGroup rootGroup = (ViewGroup) (getWindow().getDecorView());
shadowView = new View(this);
rootGroup.addView(shadowView, 0);
ViewGroup.LayoutParams params = shadowView.getLayoutParams();
//阴影宽度
params.width = (int) ((float) ConvertUtil.getWidthInPx() * 0.05f);
params.height = ConvertUtil.getHeightInPx();
shadowView.setLayoutParams(params);
//渐变背景作为阴影
shadowView.setBackgroundResource(R.drawable.shadow_grey_h);
shadowView.setTranslationX(-params.width);
rootView = rootGroup.getChildAt(1);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (shouldIntercept) {
return onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
downX = ev.getRawX();
downY = ev.getRawY();
hadJudge = false;
break;
}
case MotionEvent.ACTION_MOVE: {
if (hadJudge) break;
if (ev.getRawX() == downX) break;
if (ev.getRawX() < downX) {
//左滑
hadJudge = true;
break;
}
if (ev.getRawX() - downX >= 100) {
//超出判断距离
hadJudge = true;
break;
}
if (ev.getRawX() - downX > 50) {
//x轴右滑50~100px
float rate = (ev.getRawX() - downX) / (Math.abs(ev.getRawY() - downY));
if ((downX < 50 && rate > 0.5f) || rate > 2) {
shouldIntercept = true;
}
}
break;
}
case MotionEvent.ACTION_UP: {
downX = 0;
downY = 0;
shouldIntercept = false;
hadJudge = false;
break;
}
}
//Activity的默认分发
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
initShadow();
//测量手指抬起时的速度
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
maxFlingVelocity = ViewConfiguration.get(this).getScaledMaximumFlingVelocity();
}
velocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
lastX = event.getRawX();
break;
}
case MotionEvent.ACTION_MOVE: {
if (lastX == -1) {
lastX = event.getRawX();
break;
}
//根据手指滑动距离设置根布局偏移距离
rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX);
if (rootView.getTranslationX() < 0) rootView.setTranslationX(0);
//阴影跟随根布局移动
shadowView.setTranslationX(-shadowView.getWidth() + rootView.getTranslationX());
//下层activity跟随移动
if (lastActivity != null && lastActivity.rootView != null)
lastActivity.rootView.setTranslationX(-ConvertUtil.getWidthInPx() * 0.3f + rootView.getTranslationX() * 0.3f);
lastX = event.getRawX();
break;
}
case MotionEvent.ACTION_UP: {
//测量手指抬起时的速度
velocityTracker.computeCurrentVelocity(1000, maxFlingVelocity);
float velocityX = velocityTracker.getXVelocity();
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
//判断是否返回
if (downX < 50 && velocityX > 1000) {
//手指在左侧边落下,返回
onBack();
} else if (velocityX > 3600) {
//手指快速滑动,返回
onBack();
} else if (rootView.getTranslationX() > ConvertUtil.getWidthInPx() * 0.3) {
//滑动距离超过30%屏幕宽度,返回
onBack();
} else {
//不返回,根布局偏移归零
rootView.animate().translationX(0).setDuration(200).start();
//阴影偏移归零
shadowView.animate().translationX(-shadowView.getWidth()).setDuration(200).start();
//下层activity偏移复原
if (lastActivity != null) lastActivity.lowerActivityExitAnim();
}
lastX = -1;
shouldIntercept = false;
hadJudge = false;
downX = 0;
downY = 0;
break;
}
}
return super.onTouchEvent(event);
}
private void lowerActivityExitAnim() {
if (rootView == null) return;
rootView.animate().translationX(-ConvertUtil.getWidthInPx() * 0.3f).setDuration(300).start();
}
private void lowerActivityEnterAnim(float upperTranslationX) {
if (rootView == null) return;
float r = 1-upperTranslationX/ (float) ConvertUtil.getWidthInPx();
rootView.animate().translationX(0).setDuration(r == 0.0f ? 10 : (long) (300f * r)).start();
}
//退出
abstract public void onBack();
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# android
# 右滑返回
# Android右滑返回上一个界面的实现方法
# Android仿微信右滑返回功能的实例代码
# Android实现类似IOS右滑返回的效果(原因分析及解决办法)
# Android中SwipeBack实现右滑返回效果
# Android 实现右滑返回功能
# 重写
# 跳转
# 判断是否
# 是一个
# 要在
# 就行
# 自定义
# 只有一个
# 要比
# 所需要
# 类似于
# 含着
# 大家多多
# 大一些
# 标题栏
# 上一步
# 为左
# 偏移量
# duration
# toXDelta
相关文章:
建站之星导航菜单设置与功能模块配置全攻略
c# Task.ConfigureAwait(true) 在什么场景下是必须的
公司网站的制作公司,企业网站制作基本流程有哪些?
网站制作与设计教程,如何制作一个企业网站,建设网站的基本步骤有哪些?
专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?
建站之星2.7模板快速切换与批量管理功能操作指南
如何在IIS中新建站点并配置端口与物理路径?
建站之星如何优化SEO以实现高效排名?
宝塔建站教程:一键部署配置流程与SEO优化实战指南
大同网页,大同瑞慈医院官网?
网站制作公司排行榜,抖音怎样做个人官方网站
如何选择适配移动端的WAP自助建站平台?
制作公司内部网站有哪些,内网如何建网站?
,有什么在线背英语单词效率比较高的网站?
西安专业网站制作公司有哪些,陕西省建行官方网站?
建站之星安装需要哪些步骤及注意事项?
建站VPS推荐:2025年高性能服务器配置指南
企业微网站怎么做,公司网站和公众号有什么区别?
手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?
如何通过服务器快速搭建网站?完整步骤解析
网站规划与制作是什么,电子商务网站系统规划的内容及步骤是什么?
网站插件制作软件免费下载,网页视频怎么下到本地插件?
网站制作费用多少钱,一个网站的运营,需要哪些费用?
重庆市网站制作公司,重庆招聘网站哪个好?
如何高效生成建站之星成品网站源码?
如何选择长沙网站建站模板?H5响应式与品牌定制哪个更优?
制作网站的网址是什么,请问后缀为.com和.com.cn还有.cn的这三种网站是分别是什么类型的网站?
宝塔建站助手安装配置与建站模板使用全流程解析
建站之星如何实现网站加密操作?
建站主机空间推荐 高性价比配置与快速部署方案解析
Java解压缩zip - 解压缩多个文件或文件夹实例
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
贸易公司网站制作流程,出口贸易网站设计怎么做?
宝塔新建站点报错如何解决?
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
如何快速配置高效服务器建站软件?
天河区网站制作公司,广州天河区如何办理身份证?需要什么资料有预约的网站吗?
如何在Golang中处理模块冲突_解决依赖版本不兼容问题
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
如何优化Golang Web性能_Golang HTTP服务器性能提升方法
广平建站公司哪家专业可靠?如何选择?
外贸公司网站制作哪家好,maersk船公司官网?
Android滚轮选择时间控件使用详解
如何快速查询网址的建站时间与历史轨迹?
最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?
如何用y主机助手快速搭建网站?
网站制作大概多少钱一个,做一个平台网站大概多少钱?
建站之星CMS建站配置指南:模板选择与SEO优化技巧
如何基于PHP生成高效IDC网络公司建站源码?
免费制作海报的网站,哪位做平面的朋友告诉我用什么软件做海报比较好?ps还是cd还是ai这几个软件我都会些我是做网页的?
*请认真填写需求信息,我们会在24小时内与您取得联系。