Android滑动返回-swipebacklayout解析

交互效果

image

背景

ios7发布时有滑动返回手势操作,产品经理看到后很兴奋,立即要求android增加相同手势操作。由于android系统特性,并不支持两个activity的滑动操作。经过一番研究和挣扎,终于实现了效果。实现原理基本和下文要分析的swipebacklayout类似,我们直接来分析swipebacklayout。

使用方法

使用方法很简单,activity继承SwipeBackActivity就可以了。

实现原理

假设场景

有两个activity – A 和 B,B在A上层 ,手指滑动B返回到A。

实现原理

手指移动B页面的view,因为B被设置为透明,所以会看到A。

分析

swipebacklayout布局对比

使用swipebacklayout的activity


普通activity布局


可以看到,使用了swipebacklayout的activity会在DecorView中增加一层SwipeBackLayout(FrameLayout)。视图跟随手指滑动就是通过SwipeBackLayout来实现的。我们结合代码来看。

布局增加SwipeBackLayout(FrameLayout)

SwipeBackActivity

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    mHelper.onPostCreate();
}

SwipeBackActivityHelper

public void onPostCreate() {
    // 在DecorView下增加SwipeBackLayout(FragmentLayout)
    mSwipeBackLayout.attachToActivity(mActivity);
}

SwipeBackLayout

public void attachToActivity(Activity activity) {
    mActivity = activity;
    TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{
            android.R.attr.windowBackground
    });
    int background = a.getResourceId(0, 0);
    a.recycle();

    // 在DecorView下增加SwipeBackLayout(FragmentLayout)
    ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
    // 拿到第一个子view-decorChild
    ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
    decorChild.setBackgroundResource(background);
    // 删除子view-decorChild
    decor.removeView(decorChild);
    // 把子view-decorChild添加到SwipeBackLayout(FragmentLayout)下
    addView(decorChild);
    setContentView(decorChild);
    // 把SwipeBackLayout(FragmentLayout)添加到DecorView下
    decor.addView(this);
}    
手指滑动,移动页面

SwipeBackLayout

@Override
public boolean onTouchEvent(MotionEvent event) {
    // 是否支持手势返回
    if (!mEnable) {
        return false;
    }
    // 处理屏幕事件
    mDragHelper.processTouchEvent(event);
    return true;
}

ViewDragHelper.processTouchEvent(event),我们只分析主要的ACTION_MOVE事件。

        case MotionEvent.ACTION_MOVE: {
            if (mDragState == STATE_DRAGGING) {
                // 计算位置
                final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
                final float x = MotionEventCompat.getX(ev, index);
                final float y = MotionEventCompat.getY(ev, index);
                final int idx = (int) (x - mLastMotionX[mActivePointerId]);
                final int idy = (int) (y - mLastMotionY[mActivePointerId]);

                // 移动view
                dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);

                saveLastMotion(ev);
            } else {
                // Check to see if any pointer is now over a draggable view.
                // 省略不需要关注的代码
                ......
            }
            break;
        }

ViewDragHelper.dragTo移动view

private void dragTo(int left, int top, int dx, int dy) {
    int clampedX = left;
    int clampedY = top;
    final int oldLeft = mCapturedView.getLeft();
    final int oldTop = mCapturedView.getTop();
    // 横向移动-左右
    if (dx != 0) {
        clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
        mCapturedView.offsetLeftAndRight(clampedX - oldLeft);
    }
    // 纵向移动-上下
    if (dy != 0) {
        clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
        mCapturedView.offsetTopAndBottom(clampedY - oldTop);
    }
    // 回调-处理逻辑
    if (dx != 0 || dy != 0) {
        final int clampedDx = clampedX - oldLeft;
        final int clampedDy = clampedY - oldTop;
        mCallback
                .onViewPositionChanged(mCapturedView, clampedX, clampedY, clampedDx, clampedDy);
    }
}
  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡矣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值