Android View原理浅析——View的事件分发机制

版权声明:欢迎来到我的个人博客:http://blog.N0tExpectErr0r.cn https://blog.csdn.net/qq_21556263/article/details/82768444

Android View原理浅析——View的事件分发机制

事件分发机制是View的一个核心知识点。它也是解决滑动冲突的一个理论基础。因此掌握好View的事件分发机制是十分重要的。

点击事件的传递规则

首先要明白,这里要分析的对象就是MotionEvent,即点击事件。点击事件的分发,实际就是MotionEvent的分发过程。MotionEvent产生后,系统需要将这个事件传递给一个具体的View,这个传递过程就是分发过程。

点击事件的分发由三个很重要的方法共同完成:

  • public boolean dispatchTouchEvent(MotionEvent event):用来进行事件的分发。如果事件可以传递给当前View,此方法一定会被调用。返回的结果收到当前View的onTouchEvent及下级View的dispathTouchEvent的影响,表示是否消耗当前事件。
  • public boolean onInterceptTouchEvent(MotionEvent event):在上述方法内部调用,来判断是否拦截某事件。如果当前View拦截了某个事件,则在同一个事件序列中,此方法不会被再次调用。返回的结果表示是否拦截当前事件。
  • public boolean onTouchEvent(MotionEvent event):在dispatchTouchEvent中调用,用于处理点击事件。返回结果表示是否消耗当前事件。如果不消耗,则在同一个事件序列中,当前View无法再次接受此事件。

上面三个方法的关系可以用如下伪代码表示:

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

通过上述代码,可大概了解点击事件传递规则:

对于一个根ViewGroup来说,产生点击事件后,首先会传递给它,此时它的 dispatchTouchEvent 就会调用。如果这个ViewGroup的 onInterceptTouchEvent 方法返回true,则表示它要拦截此事件。接着事件会交给这个ViewGroup处理,即它的onTouchEvent会被调用。如果 onInterceptTouchEvent 返回false,则表示它不拦截此事件。此时当前事件会继续传递给它的子元素,然后子元素的 dispatchTouchEvent 就会调用,反复直到事件被最终处理。

当一个View需要处理事件,若它设置了 OnTouchListener,则 OnTouchListener 中的 onTouch 方法会被回调。此时如何处理看onTouch的返回值。如果返回false,则当前View的 onTouchEvent 方法被调用,反之则不会被调用。给View设置的OnTouchListener,优先级比 onTouchEvent 方法高。在 onTouchEvent 中,如果当前设置了OnClickListener,则它的onClick方法会被调用。可以看出OnClickListener的优先级最低,即处于事件传递的尾端。

一个点击事件产生后,传递过程如下:Activity->Window->View。事件总是先传递给Activity,Activity再传递给Window,最后Window再传递给顶级View。顶级View接收到事件后,按照事件分发机制去分发事件。如果一个View的onTouchEvent返回false,则它的父容器的onTouchEvent会被调用。如果所有元素都不处理这个事件,则这个事件最终传递给了Activity处理,调用Activity的onTouchEvent事件。

有关事件传递机制的一些结论:

  1. 同一个事件序列是指从手指接触屏幕的一刻起,到手指离开屏幕那一刻结束。在这个过程中产生的事件。这个事件序列以down事件开始,move事件连接,up事件结束
  2. 正常情况下,一个事件序列只能被一个View拦截且消耗。因为一旦一个元素拦截了某事件,那么同一个事件序列所有事件都会交给它处理。因此同一个事件序列中的事件不能分别由两个View同时处理。不过通过特殊手段可做到(比如一个View将本该自己处理的事件通过onTouchEvent传给其他View处理)
  3. 某个View一旦决定拦截,则这一个事件序列都会由它处理,并且它的onInterceptTouchEvent不会再被调用。
  4. 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),则同一事件序列的其他事件都不会再交给它处理,父元素的onTouchEvent将会调用。
  5. 如果View不消耗ACTION_DOWN以外的其他事件,则这个点击事件会消失。并且当前View可以持续收到后续的事件。最后消失的点击事件会传递给Activity处理。
  6. ViewGroup默认不拦截任何事件。它的onInterceptTouchEvent默认返回false
  7. View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,则它的onTouchEvent就会调用
  8. View的onTouchEvent默认会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)
  9. View的enable属性不影响onTouchEvent的默认返回值。哪怕一个View是dispable状态,只要clickable和longClickable有一个为true,它的onTouchEvent就返回true。
  10. onClick发生前提是当前View是可点击的,并且受到了down事件和up事件
  11. 事件传递是由外向内的,即事件总先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent 方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。

广告时间
我是N0tExpectErr0r,一名广东工业大学的大二学生
欢迎来到我的个人博客,所有文章均在个人博客中同步更新哦
http://blog.N0tExpectErr0r.cn

猜你喜欢

转载自blog.csdn.net/qq_21556263/article/details/82768444
今日推荐