android 右滑返回的示例代碼
類似于微信的右滑返回,在BaseActivity里利用dispatchTouchEvent()攔截右滑動(dòng)作,利用setTranslationX()實(shí)現(xiàn)動(dòng)畫,在DecorView里添加View作為滑動(dòng)時(shí)的左側(cè)陰影。
漸進(jìn)步驟:
- 設(shè)置activity背景透明
- 重寫finish()等方法設(shè)置activity的跳轉(zhuǎn)動(dòng)畫
- 重寫dispatchTouchEvent()攔截 所需要 右滑動(dòng)作
- 重寫onTouchEvent()給根布局設(shè)置偏移量
- 添加滑動(dòng)時(shí)上層activity的左側(cè)陰影
- 滑動(dòng)時(shí)關(guān)聯(lián)下層activity滑動(dòng)
注意:步驟中的代碼為了不關(guān)聯(lián)到后面的步驟,會(huì)與最終的有點(diǎn)不同
背景透明
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item>
activity的跳轉(zhuǎn)動(dòng)畫
根據(jù)項(xiàng)目需要,重寫用到的startActivity(Intent intent),startActivityForResult(Intent intent, int requestCode),finish()等activity跳轉(zhuǎn)和銷毀方法
@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>
攔截右滑動(dòng)作
所有的觸摸事件通過activity.dispatchTouchEvent(MotionEvent ev)向view分發(fā)。
手指在X軸方向右滑動(dòng)50~100px時(shí),判斷是否為(產(chǎn)品經(jīng)理要)右滑動(dòng)作
- 手指落點(diǎn)為全屏幕,X方向滑動(dòng)距離要比Y方向的大一些;
- 手指落點(diǎn)為左側(cè)邊,X方向滑動(dòng)距離有一些就行
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的默認(rèn)分發(fā) if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return true; }
根布局位移動(dòng)畫
根據(jù)手指滑動(dòng)距離設(shè)置根布局偏移距離,用滑動(dòng)距離和手指抬起時(shí)的速度判斷是否返回
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); } //測(cè)量手指抬起時(shí)的速度 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; } //根據(jù)手指滑動(dòng)距離設(shè)置根布局偏移距離 rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX); if (rootView.getTranslationX() < 0) rootView.setTranslationX(0); lastX = event.getRawX(); break; } case MotionEvent.ACTION_UP: { //測(cè)量手指抬起時(shí)的速度 velocityTracker.computeCurrentVelocity(1000, maxFlingVelocity); float velocityX = velocityTracker.getXVelocity(); if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } //判斷是否返回 if (downX < 50 && velocityX > 1000) { //手指在左側(cè)邊落下,返回 onBack(); } else if (velocityX > 3600) { //手指快速滑動(dòng),返回 onBack(); } else if (rootView.getTranslationX() > ConvertUtil.getWidthInPx() * 0.3) { //滑動(dòng)距離超過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); }
添加左側(cè)陰影
Activity的最頂層View為DecorView,DecorView是一個(gè)FrameLayout,里面只有一個(gè)Linearlayout,Linearlayout包含著標(biāo)題欄和自定義布局(setContentView)。
上一步跟隨手指滑動(dòng)進(jìn)行偏移的就是Linearlayout,現(xiàn)在要在DecorView里添加一個(gè)View,設(shè)置背景作為陰影,并跟隨Linearlayout進(jìn)行移動(dòng)
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; } //根據(jù)手指滑動(dòng)距離設(shè)置根布局偏移距離 rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX); if (rootView.getTranslationX() < 0) rootView.setTranslationX(0); //陰影跟隨根布局移動(dòng) 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(); } ... } } ... }
關(guān)聯(lián)下層activity滑動(dòng)
- 保存所有的activity以獲取下層activity
- 給下層activity添加退出和進(jìn)入的動(dòng)畫
- 在上層activity滑動(dòng)時(shí)調(diào)用下層滑動(dòng)
獲取下層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的退出、進(jìn)入動(dòng)畫
private void lowerActivityExitAnim() { if (rootView == null) return; //只移動(dòng)30% rootView.animate().translationX(-ConvertUtil.getWidthInPx() * 0.3f).setDuration(300).start(); } private void lowerActivityEnterAnim(float upperTranslationX) { if (rootView == null) return; //保證滑動(dòng)退出時(shí),上下層時(shí)間同步 float r = 1-upperTranslationX/ (float) ConvertUtil.getWidthInPx(); rootView.animate().translationX(0).setDuration((long) (300f * r)).start(); }
在跳轉(zhuǎn)時(shí),調(diào)用下層activity的退出、進(jìn)入動(dòng)畫
@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滑動(dòng)時(shí)關(guān)聯(lián)下層滑動(dòng)
@Override public boolean onTouchEvent(MotionEvent event) { ... switch (event.getAction()) { ... case MotionEvent.ACTION_MOVE: { ... //根據(jù)手指滑動(dòng)距離設(shè)置根布局偏移距離 rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX); if (rootView.getTranslationX() < 0) rootView.setTranslationX(0); //陰影跟隨根布局移動(dòng) shadowView.setTranslationX(-shadowView.getWidth() + rootView.getTranslationX()); //下層activity跟隨移動(dòng) 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偏移復(fù)原 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的默認(rèn)分發(fā) if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return true; } @Override public boolean onTouchEvent(MotionEvent event) { initShadow(); //測(cè)量手指抬起時(shí)的速度 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; } //根據(jù)手指滑動(dòng)距離設(shè)置根布局偏移距離 rootView.setTranslationX(rootView.getTranslationX() + event.getRawX() - lastX); if (rootView.getTranslationX() < 0) rootView.setTranslationX(0); //陰影跟隨根布局移動(dòng) shadowView.setTranslationX(-shadowView.getWidth() + rootView.getTranslationX()); //下層activity跟隨移動(dòng) 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: { //測(cè)量手指抬起時(shí)的速度 velocityTracker.computeCurrentVelocity(1000, maxFlingVelocity); float velocityX = velocityTracker.getXVelocity(); if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } //判斷是否返回 if (downX < 50 && velocityX > 1000) { //手指在左側(cè)邊落下,返回 onBack(); } else if (velocityX > 3600) { //手指快速滑動(dòng),返回 onBack(); } else if (rootView.getTranslationX() > ConvertUtil.getWidthInPx() * 0.3) { //滑動(dòng)距離超過30%屏幕寬度,返回 onBack(); } else { //不返回,根布局偏移歸零 rootView.animate().translationX(0).setDuration(200).start(); //陰影偏移歸零 shadowView.animate().translationX(-shadowView.getWidth()).setDuration(200).start(); //下層activity偏移復(fù)原 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(); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android popuwindow點(diǎn)擊外部窗口不消失的實(shí)例
下面小編就為大家?guī)硪黄猘ndroid popuwindow點(diǎn)擊外部窗口不消失的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04Android實(shí)現(xiàn)隱私政策彈窗與鏈接功能
現(xiàn)在幾乎所有的應(yīng)用市場(chǎng)都要求應(yīng)用上架需要用戶協(xié)議/隱私政策,本篇內(nèi)容將介紹如何在APP內(nèi)植入一個(gè)隱私政策彈窗與鏈接,對(duì)Android隱私政策彈窗實(shí)現(xiàn)代碼感興趣的朋友跟隨小編一起看看吧2021-07-07Android camera實(shí)時(shí)預(yù)覽 實(shí)時(shí)處理,人臉識(shí)別示例
本篇文章主要介紹了Android camera實(shí)時(shí)預(yù)覽 實(shí)時(shí)處理,面部認(rèn)證示例,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01Android編程UI設(shè)計(jì)之GridView和ImageView的用法
這篇文章主要介紹了Android編程UI設(shè)計(jì)之GridView和ImageView的用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android中GridView和ImageView組件的相關(guān)方法使用技巧,需要的朋友可以參考下2016-01-01移動(dòng)端html5圖片上傳方法【更好的兼容安卓IOS和微信】
這篇文章主要為大家詳細(xì)介紹了移動(dòng)端html5圖片上傳方法,更好的兼容安卓IOS和微信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06Android Service自啟動(dòng)注意事項(xiàng)分析
這篇文章主要介紹了Android Service自啟動(dòng)注意事項(xiàng),結(jié)合實(shí)例分析了Android Service自啟動(dòng)過程中屬性設(shè)置的相關(guān)技巧,需要的朋友可以參考下2016-03-03Android ExpandableListView使用方法案例詳解
這篇文章主要介紹了Android ExpandableListView使用方法案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08Android中Viewpager禁止滑動(dòng)的實(shí)現(xiàn)
有時(shí)候在開發(fā)中會(huì)遇到一些特別的要求,如在ViewPager中嵌入ListView,或者再嵌入一個(gè)ViewPager,那么在滑動(dòng)的時(shí)候就會(huì)造成被嵌入的XXView不能滑動(dòng)了,那么就把最外層的ViewPager禁止滑動(dòng)吧,本文就介紹了Android中Viewpager禁止滑動(dòng)的實(shí)現(xiàn)方法,需要的朋友可以參考。2017-05-05Android項(xiàng)目類似淘寶 電商 搜索功能,監(jiān)聽軟鍵盤搜索事件,延遲自動(dòng)搜索,以及時(shí)間排序的搜索歷史記錄的實(shí)現(xiàn)
本篇文章主要介紹了Android實(shí)現(xiàn)類似淘寶、電商、搜索功能(監(jiān)聽軟鍵盤搜索事件,延遲自動(dòng)搜索,以及時(shí)間排序的搜索歷史記錄),感興趣的小伙伴們可以參考一下。2016-10-10