Android面试View的事件分发

Posted Rose J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android面试View的事件分发相关的知识,希望对你有一定的参考价值。

分发的事件

首先事件分发主要分为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwLZqHkE-1621263277866)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620567509517.png)]

事件分发发生在ViewGroup中的dispatchTouchEvent中

action_move会触发多次

View继承关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iT1tPkBk-1621263277868)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620567819448.png)]

view中dispatchTouchEvent用来处理事件

viewgroup中dispatchTouchEvent用来分发事件,不处理事件,viewgroup直接交给view去处理事件

事件分发处理流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-16GnC3Qe-1621263277869)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620568140748.png)]

viewgroup先走分发流程,再走处理流程

view只走处理流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owtUgeo3-1621263277872)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621144121153.png)]

点击事件的整个分发流程大概:

首先点击事件,压到屏幕后,触发触屏,导致驱动,再到内核这一块,然后再慢慢的到安卓底层,安卓底层再到源码的话,安卓是直接先到activity的dispatchTouchEvent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JnG3XSX-1621263277874)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620568762791.png)]

然后通过getWindow(window是一个抽象类,他只有一个实现类就是PhoneWindow)调用PhoneWindow的superDispatchTouchEvent方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0JSG4svb-1621263277875)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620568994459.png)]

然后会调用DecorView的superDispatchTouchEvent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传下上传(icW18sTvnydp-1621263277875)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620569132632.png)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620569132632.png)]

然后调用父类的dispatchTouchEvent方法,这里父类是FrameLayout,但其实是调用的viewgroup的dispatchTouchEvent方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GjY7Ti3M-1621263277875)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620569953929.png)]

我们的activity,以及viewGroup容器,都是通过viewGroup的dispatchTouchEvent来进行分发的,然后再由view.dispatchTouchEvent进行处理

注意:

分发是从底层的view一层一层分发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wXKHJR0v-1621263277876)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620569775806.png)]

处理是从顶层的view一层一层往上处理的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DNFmSIQa-1621263277877)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1620569824678.png)]

处理事件最先处理的是我们最先看到的容器,而分发流程正好相反

事件分发

现在来梳理一下事件分发

我们其实可以想象我们的屏幕是一个立体坐标系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0X3IP7j-1621263277878)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621145346447.png)]

首先点击事件产生时,先进入的是activity

activity的dispatchTouchEvent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIUgqvdK-1621263277878)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621145599184.png)]

然后交给PhoneWindow的superDispatchTouchEvent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTmM9PBK-1621263277879)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621145877712.png)]

然后再到DecorView的superDispatchTouchEvent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCzBDSNB-1621263277879)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621145949783.png)]

然后调用父类的dispatchTouchEvent

DecorView是继承FrameLayout的,但是FrameLayout没有重写dispatchTouchEvent,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWS8bv5d-1621263277880)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621146042215.png)]

所以就到了我们的viewgroup的dispatchTouchEvent

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8iWccTY-1621263277880)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621146227146.png)]

而viewgroup是重写了父类的dispatchTouchEvent,所以可以用来进行事件分发,而事件处理是交给了父类,也就是view

super.dispatchTouchEvent -->view.dispatchTouchEvent

继续回到事件分发

顶级View对点击事件的分发过程

点击事件到达顶级View(一般是ViewGroup)以后,会调用ViewGroup的dispatchTouchEvent方法,主要逻辑是这样的,

如果顶级ViewGroup拦截事件也就是onInterceptTouchEvent返回ture,那么事件就交给ViewGroup处理,

(处理:这时如果ViewGroup的setOnTouchListener被调用的话,则OnTouch会被调用,否则OntouchEvent会被调用,也就是说都调用的情况下,onTouch会屏蔽掉onTouchEvent。在onTouchEvent中,如果设置了setOnClickListener,那么onClick会被调用,如果onTouch返回ture,那么不会调用onTouchEvent,也就不会调用onClick)

如果顶级ViewGroup不拦截事件,那么事件会传递给它所在的点击事件链上的子View,这时子View的dispatchTouchEvent会被调用,到此为止,事件已经从顶级View传递给了下一层View,接下来的传递过程和顶级View是一致的。如此循环就完成了整个事件的分发。

事件处理

我们先创建一个Button,然后通过点击事件调用button的onClick和onTouch方法

如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RuJuPLKo-1621263277882)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621046821410.png)]

在这里提出三个问题:

1.onTouch的返回值 有什么用?

2.onClick和onTouch哪个会先调用?

3.在哪里调用?

event对应事件
ACTION_DOWN             = 0;//按下
ACTION_UP               = 1;//离开
ACTION_MOVE             = 2;//滑动
ACTION_CANCEL           = 3;//事件被上层拦截

如果onTouch返回ture,会出现什么情况?onClick和onTouch方法中的日志都会打印吗?

答案如下:只有onTouch方法中log会打印,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cPavHchb-1621263277883)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621048058014.png)]

如果onTouch返回false呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bQJFqTM-1621263277883)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621048207970.png)]

这里是onTouch和onClick都会执行,而且 注意是onTouch先执行,

—>可见onTouch的返回值决定oclick是否执行

我们的事件处理在view.dispatchTouchEvent里面,主要是在这段代码里执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qB2Toh4T-1621263277884)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621062441847.png)]

最外层的if判断点击事件是否安全,一般来说我们的点击事件都是安全的。

然后判断mListenerInfo是否为空。这里我们需要看到前面的

setOnClickListener或者setOnTouchListener方法里面,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TNBjCEvQ-1621263277884)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621063017464.png)]

再看到getListenerInfo里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zDnZn7aJ-1621263277885)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621063030070.png)]

这里有一个单例模式,如果mListenerInfo不为空那么直接返回,否则创建一个mListenerInfo。

所以mListenerInfo肯定不为空。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2xfve1XS-1621263277885)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621062441847.png)]

再看到li.mOnTouchListener != null这个判断。

这个地方其实在

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubxYpwUu-1621263277886)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621064112149.png)]

调用方法的时候就已经new好了。所以这里也是不为空的。

(mViewFlags & ENABLED_MASK) == ENABLED表示是否控件是可以点击的

然后就到了li.mOnTouchListener.onTouch(this, event),在这里执行了onTouch。

如果onTouch返回了false,result就会返回false。如果onTouch返回了true,result就会返回true。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBloJj2R-1621263277886)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621064748149.png)]

然后在下面接着又有一个判断,这里的意思是result为false的话onTouchEvent就会执行,为true就不会执行

所以我们可以推测onclick方法在onTouchEvent方法里面执行

进一步推测onTouchEvent方法不一定是执行的。当onTouch返回ture,result返回ture的时候就不会。、

再回到onclick的执行,我们可以发现log日志打印,onclick是在ACTION_UP 之后执行的,所以我们可以看到switch判断下

case MotionEvent.ACTION_UP:中的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dx9wbNlF-1621263277887)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621066366243.png)]

看到这个PerformClick方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-igiktbGb-1621263277887)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621066393012.png)]

然后进入performClickInternal方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cphJtWcp-1621263277888)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621066415132.png)]

继续performClick方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeFO3Iy3-1621263277889)(C:\\Users\\Lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\1621066439597.png)]

诶,有没有一种拨开云雾见光明的感觉

playSoundEffect(SoundEffectConstants.CLICK);//对点击声音的处理

事件处理总结

总结顺序就是

dispatchTouchEvent —>onTouch —> onTouchEvent —> onClick

如果onTouch返回true,就不会继续了

以上是关于Android面试View的事件分发的主要内容,如果未能解决你的问题,请参考以下文章

Android View 事件分发机制

Android事件分发机制五:面试官你坐啊

Android面试老生常谈的 View 事件分发机制,看这一篇就够了!

Android面试老生常谈的 View 事件分发机制,看这一篇就够了

Android面试老生常谈的 View 事件分发机制,看这一篇就够了

Android面试收集录6 事件分发机制