Android部分事件分发机制篇

可以结合另一篇源码分析:Android事件分发机制的源码分析。 -

1、为什么有事件分发机制?

屏幕上的View可能会重叠在一起,当有多个View可以响应点击事件,要用事件分发机制解决这问题。

2、事件分发相关知识

2.1事件分发究竟是什么?

2.1.1 MotionEvent

当用户点击屏幕里View或者ViewGroup的时候,将会产生一个事件对象,这个事件对象就是MotionEvent对象(事件的类型,触摸的位置,以及触摸的时间)。

MotionEvent的值有:

MotionEvent.ACTION_DOWN:用户触摸View&ViewGroup。

MotionEvent.ACTION_MOVE:用户手指移动View&ViewGroup。

MotionEvent.ACTION_UP:用户手指离开屏幕。

MotionEvent.ACTION_CANCEL:事件退出了,不是用户导致的。

MotionEvent.ACTION_CANCEL是什么?

当控件收到前驱事件之后,如果后面事件被父控件拦截,那么当前当前控件会收到 CANCEL事件,并且把这个事件会传递给它的子事件。

产生的条件:

父View收到ACTION_DOWN,如果没有拦截事件,则ACTION_DOWN前驱事件被子View接收,父View后续事件会发送到子View。

如果在父View中拦截ACTION_UP或ACTION_MOVE,在第一次父View拦截消息的瞬间,父View指定子View不接受后续消息了,

同时子View会收到ACTION_CANCEL事件。

2.1.2 事件分发的本质(定义)

本质将点击屏幕产生的MotionEvent对象传递到某个具体的View然后处理消耗这个事件的整个过程。

2.1.3 事件怎么产生&事件分发产生的事件在哪些对象之间传递

用户触摸,滑动,离开屏幕时,这个时候就产生了事件,Android系统将事件封装为MotionEvent对象(事件的类型,事件触发的时间,以及触摸在屏幕的哪个位置等)。

MotionEvent对象在哪些对象之间传递呢?Activity&ViewGroup&View。

2.1.4 三个重要的有关事件分发的方法

1)dispatchTouchEvent

-> 若此 View接受到事件,此方法一定被调用。返回结果受此 View的onTouchEvent和下级的dispatchTouchEvent方法影响,表示是否消耗此事件。

2)onInterceptTouchEvent(只有 ViewGroup有。)

-> 在上述方法dispatchTouchEvent内部调用,用来判断是否拦截某个事件。

  当前View拦截了某个事件,此方法不会被再次调用,返回结果表示是否拦截当前事件。

3)onTouchEvent

-> 在dispatchTouchEvent内部调用,用来处理点击事件,返回结果表示是否消耗当前事件,

  如果不消耗,当前View无法再次接收到事件。

大致逻辑流程(伪代码):

public boolean dispatchTouchEvent(MotionEvent ev){

boolean consume = false;//记录返回值

if(onInterceptTouchEvent(ev)){//判断是否拦截此事件

consume = onTouchEvent(ev);//如果当前确认拦截此事件,那么就处理这个事件

}else{

consume = child.dispatchToucnEvent(ev);//如果当前确认不拦截此事件,那么就将事件分发给下一级

}

return consume;

}

小结:

  从 ViewGroupA开始,点击事件产生,A.dispatchTouch()被调用。

-> 情况1:要拦截,A.onInterceptTouchEvent()返回true。 A.onTouchEvent()然后被调用,自己处理。

-> 情况2:不拦截,A.onInterceptTouchEvent()返回false。调用子View.dispatchTouchEvent()再进行一样的判断。


View处理事件伪代码:

View.OnTouchListener() {

boolean result; //记录返回值

if(onTouch()){

View.onTouchEvent()不调用。

}else{

View.onTouchEvent()调用。

}

    }

在onTouchEvent()中,若设置onClickListener则,onClick()也会被调用。

传递过程:Activity–>Window–>View,View在按照事件分发机制分发事件。如果最底端的 View.onTouchEvent()返回false,则

  回调上一层的 onTouchEvent(),最终会返回给 Activity。

注意:

1)正常情况,一个事件只被一个 View拦截消耗。如果硬要两个 View去处理,在ViewA的.onTouchEvent()强行传递给ViewB处理。

2)某 View决定拦截,onInterceptTouchEvent()不会被调用。

3)某 View开始处理事件,如果不消耗 ACTION_DOWN事件(onTouchEvent返回了false),那么同一序列中的事件都不会再给它处理了。

4)View不消耗ACTION_DOWN以外的事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会调用,并且当前View可以持续收到后续的事件,

  最终这些消失的点击事件会传递给Activity处理。


5)ViewGroup默认不拦截任何事件。Android源码中ViewGroup的onInterceptTouchEvent方法默认返回false。

6)View没有onInterceptTouchEvent方法,一旦点击事件传递给它,那么它的onTouchEvent方法就会被调用。

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

  View的longClickable属性默认为false,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认为false。

8)onClick会发生的前提是当前View是可点击的,并且它接收到了down和up事件。

9)事件传递由父->子,子通过 requestDisallowInterTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。


参考网址:

https://blog.csdn.net/ClAndEllen/article/details/79365369

https://blog.csdn.net/vansbelove/article/details/78416791

https://www.jianshu.com/p/e99b5e8bd67b

https://www.jianshu.com/p/8bc0765dffc9

https://www.cnblogs.com/Claire6649/p/5947139.html

https://blog.csdn.net/mydreamongo/article/details/30465613


上的

你可能感兴趣的:(Android部分事件分发机制篇)