您的位置:首页 > 编程语言

100行代码实现Activity右滑退出

2016-04-13 20:18 246 查看
很多Android App上已经增加了Activity右滑退出的效果,这个功能通常可以叫做SwipeBackActivity。这个效果最早来源于iOS,这样的操作优化确实提升了用户体验。在github上大概看了一下,实现很多也比较复杂。本文将介绍一种简单的方法来实现这个功能,只需在页面中调用两行代码就可以轻松搞定,对页面基本没有侵入性。

先来看一下最终的效果图



正式介绍代码之前,先来说明讲基本原理。我们知道在Android的PhoneWindow中包含一个DecorView,它是整个Activity的根View,实际上就是一个FrameLayout。DecorView中又会一个子View(一个资源id名字为content的FrameLayout),我们称之为contentView,它是我们页面布局的parent布局,即我们通过setContentView()方法设置的布局就是直接添加到这个id为content的FrameLayout上。如果将我们自己定义的View称作userView,那么DecorView、contentView和userView这三个View之间就是依次包含的关系。

有了上面的这些概念,就可以很容易的实现我们的功能。我们只需要将DecorView、contentView背景设置成透明,然后将userView根据用户的手势向右进行移动,当userView全部移动出屏幕之后将Activity销毁,就形成了Activity右滑退出的假象。

下面来看一下具体的代码实现。

private int mScreenWidth;
private int mTouchSlop;

private boolean isMoving = false;
private float mInitX;
private float mInitY;

private ViewGroup decorView;//窗口根布局
private ViewGroup contentView;//content布局
private ViewGroup userView;//用户添加的布局

private ArgbEvaluator evaluator;
private ValueAnimator mAnimator;
private VelocityTracker mVelTracker;

public SwipeBackController(final Activity activity) {
mScreenWidth = activity.getResources().getDisplayMetrics().widthPixels;
mTouchSlop = ViewConfiguration.get(activity).getScaledTouchSlop();
evaluator = new ArgbEvaluator();

decorView = (ViewGroup) activity.getWindow().getDecorView();
decorView.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#00ffffff")));
contentView = (ViewGroup) activity.findViewById(android.R.id.content);
userView = (ViewGroup) contentView.getChildAt(0);

mAnimator = new ValueAnimator();
mAnimator.setDuration(ANIMATION_DURATION);
mAnimator.setInterpolator(new DecelerateInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int x = (Integer) valueAnimator.getAnimatedValue();
if (x >= mScreenWidth) {
activity.finish();
}

handleView(x);
handleBackgroundColor(x);
}
});
}

public void handleView(int x) {
userView.setTranslationX(x);
}
/**
* 控制背景颜色和透明度
* @param x
*/
private void handleBackgroundColor(float x) {
int colorValue = (int) evaluator.evaluate(x / mScreenWidth,
Color.parseColor("#dd000000"), Color.parseColor("#00000000"));
contentView.setBackgroundColor(colorValue);
Log.d(TAG, "x is " + x);
}


首先获得了屏幕宽度,获得TouchSlop增加滑动判断的准确性,否则任何微小的滑动都会被误认为是用户的滑动行为。ArgbEvaluator用来对变化的颜色求值。然后分别获得DecorView、contentView和userView,并将DecorView的背景色设置为透明。

重点来看一下DecorView、contentView和userView的获取方法。

DecorView:通过getWindow()的getDecorView()方法获得。
contentView:之前提到contentView在系统中的资源id为content,可以直接通过findViewById(android.R.id.content)来获得这个View。

userView:userView是直接添加到contentView上的,通过contentView.getChildAt(0)得到的就是userView。

然后定义用于userView的属性动画,在属性动画的监听方法中,设置contentView的背景色不断变化,并且设置userView的偏移量。当属性动画的值大于屏幕宽度时,将Activity销毁。

public boolean processEvent(MotionEvent event) {
getVelocityTracker(event);

if (mAnimator.isRunning()) {
return true;
}

int pointId = -1;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitX = event.getRawX();
mInitY = event.getRawY();
pointId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if (!isMoving) {
float dx = Math.abs(event.getRawX() - mInitX);
float dy = Math.abs(event.getRawY() - mInitY);
if (dx > mTouchSlop && dx > dy && mInitX < DEFAULT_TOUCH_THRESHOLD) {
isMoving = true;
}
}
if (isMoving) {
handleView((int) event.getRawX());
handleBackgroundColor(event.getRawX());
}

break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
int distance = (int) (event.getRawX() - mInitX);

mVelTracker.computeCurrentVelocity(1000);
//获取x方向上的速度
float velocityX = mVelTracker.getXVelocity(pointId);

Log.d(TAG, "mVelocityX is " + velocityX);
if (isMoving && Math.abs(userView.getTranslationX()) >= 0) {
if (distance >= mScreenWidth / 4 || velocityX > 1000f) {
mAnimator.setIntValues((int) event.getRawX(), mScreenWidth);
} else {
mAnimator.setIntValues((int) event.getRawX(), 0);
}
mAnimator.start();
isMoving = false;
}

mInitX = 0;
mInitY = 0;

recycleVelocityTracker();
break;
}
return true;
}
在onTouchEvent中根据当前移动的距离,调用handleView和handleBackgroundColor方法分别来设置userView的位置和contentView的背景色。当ACTION_UP的时候,会根据当时用户手势滑动的速度和当前已经划过的距离去判断,是否继续滑动销毁Activity还是恢复页面的初始状态。然后根据判断结果去执行动画。

public class SecondActivity extends Activity {

private SwipeBackController swipeBackController;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_second);

swipeBackController = new SwipeBackController(this);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
if (swipeBackController.processEvent(ev)) {
return true;
} else {
return super.onTouchEvent(ev);
}
}
}


最后在需要实现右滑退出的Activity页面,创建一个SwipBackController的对象,然后在OnTouchEvent中将MotionEvent传给它的processEvent()方法就可以了。我们并没有给Activity设置什么属性,也不需要继承什么类,就可以轻松实现这个功能。

完整代码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: