Skip to content

Latest commit

 

History

History
93 lines (67 loc) · 3.79 KB

事件分发机制.md

File metadata and controls

93 lines (67 loc) · 3.79 KB

事件分发机制

点击事件的传递规则

public boolean dispatchTouchEvent(MotionEvent event)

用来进行事件的分发。如果事件能够传递给当前 View,那么此方法一定会被调用,返回结果受当前 View 的 onTouchEvent() 和 下级 View 的 dispatchTouchEvent()方法的影响,表示是否消耗当前事件。

public boolean onInterceptTouchEvent(MotionEvent ev)

用来判断是否拦截某个事件,如果当前 View 拦截了某个事件,那么同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。

public boolean onTouchEvent(MotionEvent event)

dispatchTouchEvent() 中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前 View 无法再次接收到事件。

三者关系:

public boolean dispatchTouchEvent(MotionEvent ev) {
  boolean consume = false;
  if(onInterceptTouchEvent(ev)) {
    consume = onTouchEvent(ev);
  } else {
    consume = child.dispatchTouchEvent(ev);
  }
  return consume;
}

当一个 View 需要处理事件时,如果它设置了 OnTouchListener,那么 OnTouchListener 中的 onTouch() 方法就会被回调。 这时事件如何处理还要看 onTouch() 的返回值,如果返回 false,则当前 View 的 onTouchEvent() 方法就会被调用;如果返回 true,那么 onTouchEvent() 不会被调用。在 onTouchEvent() 方法中,如果当前设置的有 OnClickListener,那么它的 onClick() 方法就会被调用。

点击事件的传递顺序:Activity -> Window -> View

如果一个 View 的 onTouchEvent() 返回 false,那么它的父容器的 onTouchEvent() 将会被调用。如果所有的元素都不处理这个事件,那么这个事件将会最终传递给 Activity 处理,即 Activiyt 的 onTouchEvent() 会被调用。

ViewGroup 默认不拦截任何点击事件

View 的 onTouchEvent() 默认都会消耗事件(返回 true),除非是不可点击的。(clickable 和 longClickable 同时为 false)

View 的 enable 属性不影响 onTouchEvent() 的默认返回值

通过 requestDisallowInterceptTouchEvent() 可以在子元素中干预父元素的事件分发。

子元素调用了 requestDisallowInterceptTouchEvent() 之后,父元素将无法拦截除了 ACTION_DOWN 以外的其他事件。为什么除了 ACTION_DOWN,原因是因为 ViewGroup 在分发时间的时候,如果是 ACTION_DOWN 就会 重置 FLAG_DISALLOW_INTERCEPT 这个标记位。因此,当面对 ACTION_DOWN 事件时,ViewGroup 总是会调用自己的 onInterceptTouchEvent() 方法来询问自己是否要拦截事件。

滑动冲突

外部拦截法

重写父容器的 onInterceptTouchEvent() 方法

public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean intercepted = false;
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_MOVE:
            if (true)    //如果父容器需要点击事件
                intercepted = true;
            else
                intercepted = false;
            break;
        case MotionEvent.ACTION_UP:
            break;

    }
    return intercepted;
}

内部拦截法

重写子元素的 dispatchTouchEvent() 方法,并调用 requestDisallowInterceptTouchEvent() 方法

switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
        getParent().requestDisallowInterceptTouchEvent(true);
        break;
    case MotionEvent.ACTION_MOVE:
        if (true) {    //如果父容器需要点击事件
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        break;
    case MotionEvent.ACTION_UP:
        break;

    }
    return super.dispatchTouchEvent(ev);
}