android 右滑返回的示例代码
发布时间 - 2026-01-11 03:10:26 点击率:次类似于微信的右滑返回,在BaseActivity里利用dispatchTouchEvent()拦截右滑动作,利用setTranslationX()实现动画,在DecorView里添加View作为滑动时的左侧阴影。
渐进步骤:
- 设置activity背景透明
- 重写finish()等方法设置activity的跳转动画
- 重写dispatchTouchEvent()拦截 所需要 右滑动作
- 重写onTouchEvent()给根布局设置偏移量
- 添加滑动时上层activity的左侧阴影
- 滑动时关联下层activity滑动
注意:步骤中的代码为了不关联到后面的步骤,会与最终的有点不同
背景透明
<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时,判断是否为(产品经理要)右滑动作
- 手指落点为全屏幕,X方向滑动距离要比Y方向的大一些;
- 手指落点为左侧边,X方向滑动距离有一些就行
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以获取下层activity
- 给下层activity添加退出和进入的动画
- 在上层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
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
香港服务器WordPress建站指南:SEO优化与高效部署策略
javascript中数组(Array)对象和字符串(String)对象的常用方法总结
香港服务器租用每月最低只需15元?
nodejs redis 发布订阅机制封装实现方法及实例代码
电商网站制作价格怎么算,网上拍卖流程以及规则?
太平洋网站制作公司,网络用语太平洋是什么意思?
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
美食网站链接制作教程视频,哪个教做美食的网站比较专业点?
Laravel如何实现事件和监听器?(Event & Listener实战)
C++用Dijkstra(迪杰斯特拉)算法求最短路径
高配服务器限时抢购:企业级配置与回收服务一站式优惠方案
Win11怎么关闭专注助手 Win11关闭免打扰模式设置【操作】
Laravel怎么实现支付功能_Laravel集成支付宝微信支付
Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法
如何在腾讯云服务器上快速搭建个人网站?
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】
如何在服务器上配置二级域名建站?
Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】
Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
Laravel Seeder填充数据教程_Laravel模型工厂Factory使用
详解Android中Activity的四大启动模式实验简述
如何在橙子建站中快速调整背景颜色?
大型企业网站制作流程,做网站需要注册公司吗?
LinuxCD持续部署教程_自动发布与回滚机制
昵图网官网入口 昵图网素材平台官方入口
Laravel如何配置和使用缓存?(Redis代码示例)
音响网站制作视频教程,隆霸音响官方网站?
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
JS中对数组元素进行增删改移的方法总结
如何基于PHP生成高效IDC网络公司建站源码?
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
高端建站三要素:定制模板、企业官网与响应式设计优化
如何在万网主机上快速搭建网站?
javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】
Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
实现点击下箭头变上箭头来回切换的两种方法【推荐】
Firefox Developer Edition开发者版本入口
如何登录建站主机?访问步骤全解析
Laravel如何创建自定义Facades?(详细步骤)
Laravel事件和监听器如何实现_Laravel Events & Listeners解耦应用的实战教程
深圳防火门网站制作公司,深圳中天明防火门怎么编码?
JavaScript模板引擎Template.js使用详解
香港服务器网站卡顿?如何解决网络延迟与负载问题?
北京网站制作公司哪家好一点,北京租房网站有哪些?
Laravel用户密码怎么加密_Laravel Hash门面使用教程
Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例

