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應(yīng)用開發(fā)中CardView的初步使用指南
這篇文章主要介紹了Android應(yīng)用開發(fā)中CardView的初步使用指南,CardView主要處理一些卡片型的視圖布局,需要的朋友可以參考下2016-02-02
fragment實(shí)現(xiàn)隱藏及界面切換效果
這篇文章主要為大家詳細(xì)介紹了fragment實(shí)現(xiàn)隱藏及界面切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
android中WebView和javascript實(shí)現(xiàn)數(shù)據(jù)交互實(shí)例
這篇文章主要介紹了android中WebView和javascript實(shí)現(xiàn)數(shù)據(jù)交互實(shí)例,需要的朋友可以參考下2014-07-07
flutter PositionedTransition實(shí)現(xiàn)縮放動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了flutter PositionedTransition實(shí)現(xiàn)縮放動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
Android Studio 下 Flutter 開發(fā)環(huán)境搭建過程
這篇文章主要介紹了Android Studio 下 Flutter 開發(fā)環(huán)境搭建/Flutter / Dart 插件安裝 | Flutter SDK 安裝 | 環(huán)境變量配置 | 開發(fā)環(huán)境檢查,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下2020-03-03
Android使用webView長(zhǎng)按保存下載網(wǎng)絡(luò)圖片
這篇文章主要為大家詳細(xì)介紹了Android使用webView長(zhǎng)按保存下載網(wǎng)絡(luò)圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
android編程實(shí)現(xiàn)的自定義注釋模板實(shí)例
這篇文章主要介紹了android編程實(shí)現(xiàn)的自定義注釋模板,以完整實(shí)例形式分析了Android自定義魔板的定義及具體實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下2015-11-11

