Activity右滑返回效果实现

Activity右滑返回效果实现

1. 实现思路

  • 参考文章(http://blog.csdn.net/xiaanming/article/details/20934541
  • 我们想要对一个Activity整体进行滑动,我们就需要对这个Activity布局文件的顶层布局的父布局进行滑动,我们应该在这个父布局上下功夫。通过源码可以知道,其实Android系统在加载自定义布局时,还会对我们的布局文件的最外层套一个FrameLayout,所以我们其实就是对FrameLayout进行滚动就行了,而这个FrameLayout我们可以通过window对象的getDecorView()方法获取到。
  • 滑动相关操作我们借助Scroller类来做处理。
  • 这次要实现的效果,就是在一个自定义View上结合Scroller类来做处理,从而达到想要的效果。在使用时,我们只要把Activity的顶层父布局替换成这个View就可以了。

2. 实现过程

2.1 实现的效果

  • 可以设置屏幕响应区域,边缘响应还是全局响应
  • 支持设置滑动区域超过多少才执行退出
  • 支持设置一个速率,如果是快速滑动,直接退出
  • 与 listView、scrollView、viewPager 冲突解决

2.2 SwipeBackLayout的关键代码

  • Touch 事件的拦截
@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 处理ViewPager冲突问题
        ViewPager mViewPager = getTouchViewPager(mViewPagers, ev);
        Log.d(TAG, "mViewPager = " + mViewPager);

        if (mViewPager != null && mViewPager.getCurrentItem() != 0) {
            return super.onInterceptTouchEvent(ev);
        }

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = tempX = (int) ev.getRawX();
                downY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) ev.getRawX();
                // 满足此条件屏蔽SwipeBackLayout里面子类的touch事件
                if (downX <= mTouchLimit
                        && moveX - downX > mTouchSlop
                        && Math.abs((int) ev.getRawY() - downY) < mTouchSlop) {
                    return true;
                }
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }
  • Touch 事件的相关处理
@Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker != null) {
            mVelocityTracker.addMovement(event);
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getRawX();
                int deltaX = tempX - moveX;
                tempX = moveX;
                if (downX <= mTouchLimit
                        && moveX - downX > mTouchSlop
                        && Math.abs((int) event.getRawY() - downY) < mTouchSlop) {
                    mIsSwiping = true;
                }

                if (moveX - downX >= 0 && mIsSwiping) {
                    mContentView.scrollBy(deltaX, 0);
                }
                break;
            case MotionEvent.ACTION_UP:
                mIsSwiping = false;
                if (mVelocityTracker != null) {
                    mVelocityTracker.computeCurrentVelocity(1);// 判断每毫秒内滑动了多少px
                    Log.d("getXVelocity()","" +mVelocityTracker.getXVelocity(0));
                    if (mVelocityTracker.getXVelocity(0) > MOVE_SPEED_LIMIT) {
                        mIsFinished = true;
                        scrollRight(true);
                        break;
                    }
                }
                if (mContentView.getScrollX() <= -mLayoutWidth / 2) {
                    mIsFinished = true;
                    scrollRight(false);
                } else {
                    mIsFinished = false;
                    scrollOrigin();
                }
                break;
        }

        return true;
    }
  • 整体滑动的相关处理
/**
     * 滚动出界面
     * @param isNeedQuick 是否需要快速滚出
     */
    private void scrollRight(boolean isNeedQuick) {
        MeilaLog.d(TAG, "scrollRight is doing, isNeedQuick: " + isNeedQuick);
        final int delta;
        if (isNeedQuick) {
            delta = (mLayoutWidth + mContentView.getScrollX());
            // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item,duration默认250
            mScroller.startScroll(mContentView.getScrollX(), 0, -delta + 1, 0);
        } else {
            delta = (mLayoutWidth + mContentView.getScrollX());
            // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
            mScroller.startScroll(mContentView.getScrollX(), 0, -delta + 1, 0, Math.abs(delta));
        }
        postInvalidate();
    }

    /**
     * 滚动到起始位置
     */
    private void scrollOrigin() {
        MeilaLog.d(TAG, "scrollOrigin is doing");
        int delta = mContentView.getScrollX();
        mScroller.startScroll(mContentView.getScrollX(), 0, -delta, 0,
                Math.abs(delta));
        postInvalidate();
    }

    @Override
    public void computeScroll() {
        // 调用startScroll的时候scroller.computeScrollOffset()返回true
        if (mScroller.computeScrollOffset()) {
            mContentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();

            if (mScroller.isFinished() && mIsFinished) {
                releaseVelocityTracker();
                mActivity.finish();
            }
        }
    }

2.3 Activity 背景透明处理

因为该效果在当前Activity整体滑动时,要看见底部的另一个Activity,因此当前Activity的父布局背景需要设置为透明。

注意:要达到我们设想的效果,如果这里仅仅是把背景颜色设置为透明是不可行的,而需要设置Activitytheme
其中下面这两个属性的设置是必须的:
“android:windowBackground” = “android:color/transparent”
“android:windowIsTranslucent” = “true”

3. 问题总结

3.1 遇到的疑难问题

设置了属性“android:windowIsTranslucent” = “true”后,出现了以下两个问题:
- 控件所监听的点击事件会被重复响应
- 原来设置的Activity的跳转动画失效了

3.2 问题的解决

  • 点击事件的重复响应,通过拦截Touch事件,设置点击延时响应解决
private long lastClickTime = 0;
private int mDownInScreenX = 0;
private int mDownInScreenY = 0;
private int mUpInScreenX = 0;
private int mUpInScreenY = 0;

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_UP:
            Log.d("mm","ACTION_UP");
            mUpInScreenX = (int) ev.getRawX();
            mUpInScreenY = (int) ev.getRawY();
            if (Math.abs(mUpInScreenX - mDownInScreenX) < 5 &&
                    Math.abs(mUpInScreenY - mDownInScreenY) < 5) {
                Log.d("mm",""+(System.currentTimeMillis() - lastClickTime));
                if (System.currentTimeMillis() - lastClickTime < MeilaResource.MIN_CLICK_DELAY_TIME) {
                    lastClickTime = System.currentTimeMillis();
                    return true;
                }
                lastClickTime = System.currentTimeMillis();
            }
            break;
        case MotionEvent.ACTION_DOWN:
            MeilaLog.d("mm","ACTION_DOWN");
            mDownInScreenX = (int) ev.getRawX();
            mDownInScreenY = (int) ev.getRawY();
            break;
    }
    return super.dispatchTouchEvent(ev);
}
  • Activity的跳转动画通过设置android:windowAnimationStyle来解决
    进入动画:设置windowEnterAnimation
    退出动画:设置windowExitAnimation

4. 如何使用

在Activity 基类中添加以下逻辑:

if (isNeedSlipFinish()) {
            mSwipeBackLayout = (SwipeBackLayout) LayoutInflater.from(this).inflate(R.layout.slip_back_base_layout,
                    null);
            mSwipeBackLayout.attachToActivity(this);
        }

新建 Activity 继承于这个基类,即可拥有向右滑动返回效果。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值