点击手机屏幕中的一个按钮,发生了什么?
当手机点击到屏幕中的某个按钮,从手指按下,移动,抬起或取消,会组成一组事件序列,根据这些事件序列,产生一系列的触摸反馈,比如界面滑动,按钮水波纹,界面跳转等等。而这些事件序列是成组产生的,由按下ACTION_DOWN
、移动ACTION_MOVE
、抬起ACTION_UP
或取消ACTION_CANCEL
组成。其中由按下为开始,抬起或取消为结束。其中取消操作是非人为的,它是一个比较特殊的事件,在后续文章中会提到。
触摸反馈中的重要方法
Android
的触摸反馈由事件分发机制来进行产生,讲到Android
事件分发,很多时候面试都会被问到这个,笔者在这个问题上,问过别人,也被别人问过。由此可见掌握它的重要性。
要想掌握事件分发,绕不开的几个方法:
dispatchTouchEvent
()
dispatchTouchEvent
方法在Activity
、ViewGroup
,View
中都有。
- 在
Activity
中,该方法用于将事件传递到Activity
中的PhoneWindow
完成,PhoneWindow再把事件传递到整个控件树的根DecorView
,之后再由DecorView
将事件处理工作交给ViewGroup
,当事件没被处理,则继续走onTouchEvent方法ViewGroup
中,事件分发从该方法开始,主要逻辑是进行是否拦截的判断,如果拦截走自身的onTouchEvent
,如果不拦截则调用dispatchTransformedTouchEvent()
方法,这个方法会调用child或者super的dispatchTouchEvent()
,最终通过View
的onTouchEvent()/onTouch()
等方法的返回值来决定dispatchTransformedTouchEvent()
的返回值View
中,在这个方法里面会先进行OnTouchListener
的判断,判断是不是设置了该监听,再判断当前控件是不是被禁用,在判断onTouch()
方法的,如果再这里被处理,则直接返回true
,后续的onTouchEvent
方法将不会被执行,否则就调用onTouchEvent
方法处理
onTouchEvent
该方法是消费事件的主要方法,存在于view中,viewGroup默认并没有重写该方法。方法返回true表示消费事件,返回false表示不消费事件。 viewGroup分发事件时,如果没有一个子view消费事件,那么会调用自身的onTouchEvent方法来处理事件。View的dispatchTouchEvent方法中,并不是直接调用onTouchEvent方法来消费事件,而是先调用onTouchListener判断是否消费;如果onTouchListener没有消费事件,才会调用onTouchEvent来处理事件。 我们为view设置的onClickListener与onLongClickListener都是在View的dispatchTouchEvent方法中,根据具体的触摸情况被调用。
InterceptTouchEvent
该方法只存在于viewGroup中,当一个事件需要被分发到子view时,viewGroup会调用此方法检查是否要进行拦截。如果拦截则自己处理,而如果不拦截才会调用子view的
dispatchTouchEvent
方法分发事件。 方法返回true表示拦截事件,返回false表示不拦截。
触摸事件序列的顺序
皆不处理不拦截事件
打印日志较长,点击这里展开查看
EventActivity-> : dispatchTouchEvent:
EventLayout-> :dispatchTouchEvent:
EventLayout-> :onInterceptTouchEvent:
EventLayout-> :onInterceptTouchEvent: false
EventView-> :dispatchTouchEvent:
EventView-> :onTouchEvent:
EventView-> :onTouchEvent: false
EventView-> :dispatchTouchEvent: false
EventLayout-> :onTouchEvent:
EventLayout-> :onTouchEvent: false
EventLayout-> :dispatchTouchEvent: false
EventActivity-> : onTouchEvent:
EventActivity-> : onTouchEvent: false
EventActivity-> : dispatchTouchEvent: false
- 该流程图将手指点击屏幕上面某个
view
的事件,从开始到结束,进行了展示。可以看到,事件的分发以及处理是一个递归的过程。先不考虑其他拦截情况,不考虑其他的事件传递,单从activity
、viewGroup
、view
三者之间进行描述。 - 可以看到当
EventActivity
收到事件的时候,通过调用dispatchTouchEvent
,将事件传递到EventLayout
. EventLayout
调用dispatchTouchEvent
方法,先调用自己的onInterCeptTouchEvent
,判断是否拦截事件,如果拦截,则返回true
,则调用自己的onTouchEvent
方法进行处理,EventView
将不会有机会得到该事件序列,图上展示的是不拦截的情况,在此需要读者自己区分一下。继续回到流程图上展示不拦截的处理,当EventLayout
不拦截该事件序列后,事件序列会来到EventView
.EventView
通过调用dispatchTouchEvent
将事件传递到自身的onTouchEvent
,如果EventView
的onTouchEvent
在事件ACTION_DOWN
的时候不进行拦截,则onTouchEvent
返回false
,EventView
的dispatchTouchEvent
将会返回onTouchEvent
返回的结果。事件开始回传- 事件经过回传,会回到
EventLayout
的onTouchEvent
,dispatchToucnEvent
,EventActivity
的onTouchEvent
,dispatchTouchEvent
。事件结束