类似于微信的右滑返回,在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(); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。