CoordinatorLayout+ViewPager+SwipeRefreshLayout滑动事件冲突的处理

只是一个搬运工

链接

参考

代码(自己加的注释,好多不懂,勿喷)
public class NestedScrollSwipeRefreshLayout extends SwipeRefreshLayout implements NestedScrollingChild {

    private static final int INVALID_POINTER = -1;
    //最小有效滑动
    private int mTouchSlop;
    private int mActivePointerId = INVALID_POINTER;

    private NestedScrollingChildHelper mChildHelper;

    public NestedScrollSwipeRefreshLayout(Context context) {
        this(context, null);
    }

    public NestedScrollSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }


    /**
     * 实现一些跟NestedScrollingParent交互的一些方法,通知父view
     * @param axes
     * @return
     */
    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    }

    /**
     * 结束整个流程。
     */
    @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }

    /**
     * 向父view汇报滚动情况,包括子view消费的部分和子view没有消费的部分。
     * 这个函数一般在子view处理scroll后调用。
     * @param dxConsumed
     * @param dyConsumed
     * @param dxUnconsumed
     * @param dyUnconsumed
     * @param offsetInWindow
     * @return  如果父view接受了它的滚动参数,进行了部分消费,则这个函数返回true,否则为false。
     */
    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    /**
     * 一般在MotionEvent.Move里调用 通知父View滑动的距离
     *
     *
     * 由于窗体进行了移动,如果你记录了手指最后的位置,需要根据第四个参数offsetInWindow计算偏移量,
     * 才能保证下一次的touch事件的计算是正确的。
     * 一般在子view处理scroll前调用
     * @param dx
     * @param dy
     * @param consumed 第一个元素是父view消费的x方向的滚动距离;第二个元素是父view消费的y方向的滚动距离
     * @param offsetInWindow 子View的窗体偏移量
     * @return  如果父view接受了它的滚动参数,进行了部分消费,则这个函数返回true,否则为false
     */
    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

    //记住最后按下时点的y值
    private float mLastMotionY;
    private final int[] mScrollOffset = new int[2];
    private final int[] mScrollConsumed = new int[2];
    //标识是否已经开始拖动
    private boolean mIsBeginDrag = false;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                //获得触摸点id.up的时候id会失效
                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);

                final float initialDownY = getMotionEventY(ev, mActivePointerId);

                //为-1,多点触摸,之后抬起,把事件交给父布局处理
                if (initialDownY == -1) {
                    return false;
                }
                //保存按下的y值,填充给最后移动的距离
                mLastMotionY = initialDownY;
                //通知父view开始嵌套滚动
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                super.onInterceptTouchEvent(ev);
                //初始化标识
                mIsBeginDrag = false;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                //如果触摸点id 为 -1  把点击事件交给父布局处理
                if (mActivePointerId == INVALID_POINTER) {
                    return false;
                }
                //标记触摸点的y值
                final float y = getMotionEventY(ev, mActivePointerId);
                //为-1,多点触摸,之后抬起,把点击事件交给父布局处理
                if (y == -1) {
                    return false;
                }
                //计算移动的距离
                int deltaY = (int)(mLastMotionY - y);
                //更新 最后按下时点的y值
                mLastMotionY  = y;
                //移动的距离是否有效,更新 是否正在拖动的标识
                if (Math.abs(deltaY) >= mTouchSlop) {
                    mIsBeginDrag = true;
                }

                /**
                 * 正在拖动,父view消耗了部分view,就交给父view处理
                 * mScrollOffset[1] 子view窗口y轴的偏移量
                 * mScrollOffset[1] 父view消费的y轴偏移量
                 */
                if (mIsBeginDrag && dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                    mLastMotionY -= mScrollOffset[1];//修正最后按下的点的y值
                    deltaY -= mScrollConsumed[1];//修正被父view消耗后的移动的距离
                    ev.offsetLocation(0, mScrollConsumed[1]);//修正ev的位置
                    //向父View汇报,如果父view接受了参数,进行了部分消费,返回true,否则返回false
                    if (dispatchNestedScroll(0, 0, 0, deltaY, mScrollOffset)) {
                        mLastMotionY -= mScrollOffset[1];//修正最后按下的点的y值
                        ev.offsetLocation(0, mScrollOffset[1]);//修正点击的位置
                    }
                    return false;
                } else {
                    //否则正常处理
                    return super.onInterceptTouchEvent(ev);
                }
            }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP: {
                //停止嵌套滑动
                stopNestedScroll();
                //初始化相关值
                mActivePointerId = INVALID_POINTER;
                mIsBeginDrag = false;
                return super.onInterceptTouchEvent(ev);
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 返回 坐标的y值
     * 解决多点触摸的问题
     * @param ev
     * @param activePointerId
     * @return
     */
    private float getMotionEventY(MotionEvent ev, int activePointerId) {
        //得到触点的索引值,范围是 0 到 ev.getPointerCount()-1;
        final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
        if (index < 0) {
            return -1;
        }
        return MotionEventCompat.getY(ev, index);
    }


}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值