滑动冲突

MotionEvent

事件分发、拦截与消费

  • 上表中勾和叉表示的是这3种事件的相关方法在Activity、ViewGroup、View中是否含有该方法

分发流程

Activity.dispatchTouchEvent()
>PhoneWindow.superDispatchTouchEvent()
>DecorView.superDispatchTouchEvent()
>ViewGroup.dispatchTouchEvent()
>View.dispatchTouchEvent()
>View.onTouchEvent()

onTouch、onClick之间的关系

public boolean dispatchTouchEvent(MotionEvent event) {
     ........省略
      if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ........省略
}

如果li.mOnTouchListener.onTouch()为false, result也为false,那么onTouchEvent()也会执行,ACTION_UP事件里performClickInternal() > performClick() > li.mOnClickListener.onClick(),说明了onTouch()先于onClick()执行。反过来,如果onTouch()为true,那么result也为true,if判断里onTouchEvent()就不会执行,onClick()也就不会被调用了。

ACTION_DOWN事件重要方法:

  • buildTouchDispatchChildList():按照z轴对子view进行排序
  • dispatchTransformedTouchEvent():事件分发给谁来处理

事件拦截

 final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                //disallowIntercept为true,则onInterceptTouchEvent()不执行
                if (!disallowIntercept) {
                    //是否拦截事件的传递
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }

内部拦截法:

ViewParent

 @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        //dispatchTouchEvent()会对ACTION_DOWN事件调用resetTouchState()
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            super.onInterceptTouchEvent(event);
            return false;
        }
        return true;
    }

ChildView

 private int mLastX, mLastY;

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;

        return super.dispatchTouchEvent(event);
    }

外部拦截法:

ViewParent

 private int mLastX, mLastY;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    return true;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }

        return super.onInterceptTouchEvent(event);
    }

以上只是2个例子,实际案例中具体情况具体分析,但知道原理了就好解决滑动冲突问题了:
父容器拦截事件:
onInterceptTouchEvent()=true
请求父容器不要拦截事件:
getParent().requestDisallowInterceptTouchEvent(true)

你可能感兴趣的:(滑动冲突)