您好,登錄后才能下訂單哦!
類似于微信的右滑返回,在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(); }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。