類似于微信的右滑返回,在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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。