android右滑返回动画,android 右滑返回

类似于微信的右滑返回,在BaseActivity里利用dispatchTouchEvent()拦截右滑动作,利用setTranslationX()实现动画,在DecorView里添加View作为滑动时的左侧阴影。

ff0f7e8d9070

image.png

渐进步骤:

设置activity背景透明

重写finish()等方法设置activity的跳转动画

重写dispatchTouchEvent()拦截 所需要 右滑动作

重写onTouchEvent()给根布局设置偏移量

添加滑动时上层activity的左侧阴影

滑动时关联下层activity滑动

注意:步骤中的代码为了不关联到后面的步骤,会与最终的有点不同

背景透明

@android:color/transparent

true

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

android:duration="300"

android:fromXDelta="100%"

android:toXDelta="0"

android:fromYDelta="0"

android:toYDelta="0"/>

//R.anim.slide_right_out

android:duration="300"

android:fromXDelta="0"

android:toXDelta="100%"

android:fromYDelta="0"

android:toYDelta="0" />

拦截右滑动作

所有的触摸事件通过activity.dispatchTouchEvent(MotionEvent ev)向view分发。

手指在X轴方向右滑动50~100px时,判断是否为(产品经理要)右滑动作

手指落点为全屏幕,X方向滑动距离要比Y方向的大一些;

手指落点为左侧边,X方向滑动距离有一些就行

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以获取下层activity

给下层activity添加退出和进入的动画

在上层activity滑动时调用下层滑动

获取下层activity

private static ArrayList 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_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();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值