View事件的分发面试题

引言:之前一直用印象笔记来整理知识点和开发中遇到的问题,最近在换工作发现如果没什么亮点面试机会都少的可怜,所以从现在开始写博客肯定也不算晚啦~。先从最近面试可能会问到的知识点开始。首先先说一下会被问到的问题,然后讲解一下解答思路,最后给出正确答案。这些知识点肯定不能死记硬背,要把自己的理解组织好语言并描述出来。这样才能算掌握了某个知识点。废话不多说,首先说一下面试中有关view的分发常见面试题:
一 、描述一下view事件的分发机制
二 、Touch事件传递流程
三 、事件分发中的onTouch() 和onTouchEvent()有什么区别,如何使用
四、 View和ViewGroup分别有哪些事件分发相关的回调方法

第一个和第二个问题,其实可以看作是一个问题,答案是相同的,首先Touch事件是什么呢?就是触摸事件,就是当我们手指按下屏幕产生的一系列事件。Touch事件所对应的(触摸的位置和事件)被封装成了MotionEvent。我们平常说的事件分发机制,分发的对象是MotionEvent,而它的本质就是 将点击事件(MotionEvent)传递/分发到具体的View的整个过程。
第三个问题其实就是解释在事件分发中的两个方法的具体区别。
第四个问题其实就是解释事件分发在View,ViewGroup中传递的区别。
下面给出第一个问题和第二个问题的解题思路和答案。
描述一下View事件分发机制/Touch事件传递机制:
1、为什么有事件分发?
2、先解释一下事件分发的对象是什么?
3、然后说明什么是事件分发机制?它的本质是什么?
4、事件分发在什么之间传递? 
5、事件分发的具体流程?
答案(仅供参考,如有意见,欢迎指出。)
Android的View是树形结构的,多个View可能会重叠在一起,当我们点击时,这个时候这个点击事件应该给谁?为了解决这个问题,就引入了事件分发机制。 事件分发的对象,其实就是Touch(点击)事件,Touch事件的触摸位置和事件又被封装成了MotionEvent,MotionEvent又有几种类型:
MotionEvent.ACTION_DOWN
MotionEvent.ACTION_MOVE
MotionEvent.ACTION_UP
MotionEvent.ACTION_CACLE
事件分发的本质就是将点击事件(Touch)传递到具体的View并处理的整个过程。事件分发在Activity - ViewGroup -View之间传递。它的具体流程是:这里给出一张流程图来解释。
图 1.1
解释:首先,事件分发是从Activity的dispatchTouchEvent()方法开始的。这个方法的返回值是由getWindow().superDispatchTouchEvent 和onTouchEvent共同决定的。如果前者为true,那么这个方法(dispatchTouchEvent())的返回值就为true,否则会这个方法的返回值就由onTouchEvent决定。那么getWindow().superDispatchTouchEvent()这个方法是干什么的。首先getWindow()是得到window的实现类,由于window是抽象类,它的唯一实现类是PhoneWindow。实际上调用PhoneWIndow.superDispatchTouchEvent()这个方法,这个方法实际调用mDecor.superDispatchTouchEvent(), mDecor就是DecorView,根View。是PhoneWindow中的内部类。是View的最底层。它的父类是FrameLayout,而FrameLayout的父类又是ViewGroup,所以实际调用的就是ViewGroup.dispatchTouchEvent()。(将事件从Activity传递到了ViewGroup中。)Activity.onTouchEvent()这个方法一会再说。
在我们的ViewGroup中有一个拦截方法onIntercepterTouchEvent()这个方法是Activity和View没有的。那么,为什么Activity和View没有拦截方法呢?因为Activity作为事件分发的最顶端,如果Activity将事件拦截下来,就会导致整个屏幕都无法相应事件。这肯定不是我们想要的结果。而View作为事件分发的最末端,它要么就把事件消费掉,要么就不处理进行回传,它没有必要进行事件拦截。 onIntercepterTouchEvent()这个方法需要我们手动调用。
默认的返回值是false 就是不拦截事件,事件会继续向下传递。实现了事件从ViewGroup到View的传递。会调用View.dispatchTouchEvent()进行事件分发。这时候我们会手动复写onTouch()这个方法。如果实现了(setOnTouchListener)。这个方法的返回值如果是true。就表示事件被消费掉了。依次回传true给dispatchTouchEvent.这个时候可以参考下图例子1.4。如果返回值是false也可以理解当前View.onTouchEvent()返回false,表示事件没有被消费。这个时候我们就会依次通过当前View的dispatchTouchEvent询问上一级的onTouchEvent能否消费当前的View。如果最终没有ViewGroup/View消费这个View就会调用Activity.onTouchEvent()这个方法。可以参考图1.5。下面我们详细说一下Activity.onTouchEvent()这个方法。这个方法实际上做了一个判断,当mWindow.ShouldCloseOnTouch() 是否在Window边界内。如果在边界外,就会消费事件,返回true。否则返回false。
如果我们返回true拦截事件,这时候可能是因为我们点击了空白区域,没有子view进行消费事件。所以我们会调用自己的onTouch --> onTouchEvent -->performClick -->onClick 其中一个进行事件的消费。如果消费掉就会返回true,这个值会依次回传给各层级的dispatchTouchEvent.最终会告诉Activity.dispatchTouchEvent()。这时候就不会调用Activity.dispatchTouchEvent().这种拦截情况可以参考下图1.3。
例子:

图  1.3 ViewGroupA拦截并消费事件。

          图1.4事件分发到View1并被View1消费掉


图 1.5 点击View1 没有任何view消费事件


通过上面的描述我们对事件分发过程有了大致的理解想要知道详细的过程还是得看源码。下面看一下第三个问题。
三 、事件分发中的onTouch() 和onTouchEvent()有什么区别,如何使用
解决这个问题从下面几点出发
 1、onTouch()和onTouchEvent()这两个方法分别做了什么?有什么区别?如何使用。
答案:(仅供参考)
这两个方法都在View.dispatchTouchEvent()中调用。
 onTouch是 View的onTouchListener 中的方法。当实现了这个方法后,View有touch事件后就会一遍一遍调用这个方法。若一个控件不可点击即(enable = false)它的onTouch()事件永远得不到执行
onTouchEvent 是复写的方法。当复写了这个方法。屏幕有Touch事件就会一遍一遍调用这个方法。
区别就是调用的方式不同,并且onTouch()的优先级比onTouchEvent()的优先级高。当onTouch()的返回值为true时,此时事件已经被消费了,事件就不会向onTouchEvent()传递了,也不会调用onClick()(需要注意onClick是在onTouchEvent()中执行的)。只有当onTouch()的返回值为false。才会调用onTouchEvent(),只是onTouch()优先级更高。。所以它们的优先级是onTouch()>onTouchEvent()>performClick()>onClick()。
  • onTouch他的处理粒度更小,他可以处理down、move和up组成的各种事件。
  • 但是onTouchEvent()只是onClick()监听事件。
四 、View和ViewGroup分别有哪些事件分发相关的回调方法
View dispatchTouchEvent(),onTouchEvent()
ViewGroup dispatchTouchEvent(),onIntercepterTouchEvent()(这个方法一旦返回true,同一个事件列就不会在被调用了。),onTouchEvent().
需要注意 onIntercepterTouchEvent(),onTouchEvent().都是在dispatchTouchEvent(),中调用的。
再解释下View 为什么没有onIntercepterTouchEvent(),即可。
这篇文章只是针对了这几个问题,对事件分发进行讲解。在实际开发中,我们的事件分发通常是在自定义View中使用的。还是得具体问题具体分析。在实践中去理解原理才是正确的学习方法。
参考文章:
1、 Android事件分发机制 详解攻略, (源码可参考这篇文章。)

https://www.cnblogs.com/aademeng/articles/6541321.html

猜你喜欢

转载自blog.csdn.net/qtl_crazy/article/details/79888654
今日推荐