100行代码实现Activity右滑退出

来源:互联网 发布:python致命缺点 编辑:程序博客网 时间:2024/05/21 00:45

       很多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设置什么属性,也不需要继承什么类,就可以轻松实现这个功能。


完整代码下载


1 0
原创粉丝点击