事件分发流程:
Ⅰ.当手指触摸屏幕后,底层Input drive经过一系列调用后,事件传递到了DecorView dispatchTouchEvent()->
Ⅱ.在DecorView中,通过Window内部接口Callback,事件继续传递,因为Activity实现了该接口,故事件分发到Activity的dispatchTouchEvent()->
Ⅲ.Activity获取到事件后,在dispatchTouchEvent()中先将事件分发给所在的window,实际就是PhoneWindow,window又将事件交给它的顶级view(DecorView)处理,DecorView的superDispatchTouchEvent->
Ⅳ.DecorView是FrameLayout的子类,即ViewGroup的子类,自己没有处理,所以继续将事件交由 ViewGroup处理,ViewGroup的dispatchTouchEvent()
三个角色
①Activity:
只有分发dispatchTouchEvent、消费onTouchEvent两个方法。
dispatchTouchEvent->false:无人处理,直接调用onTouchEvent()消费事件
dispatchTouchEvent->true表示自己消费
②ViewGroup:
拥有分发、拦截和消费三个方法。对于一个ViewGroup来说,点击事件产生之后,dispatchTouchEvent()会被调用。
onInterceptTouchEvent->true:表示拦截当前事件,交给ViewGroup的onTouchEvent()处理。
onInterceptTouchEvent->false:表示不拦截当前事件,这时事件就会继续传递给它的子View,接着子View的dispatchTouchEvent()会被调用。
③View:
只有分发和消费两个方法。
dispatchTouchEvent->true:表示view可以处理对应的事件。
dispatchTouchEvent->false:表示view不处理这个事件,事件会被传递给父视图。
三个核心事件
①dispatchTouchEvent():
true:表示事件被当前视图消费掉。
false:表示停止往子View传递和分发。
②onInterceptTouchEvent():
false:不拦截,继续传递给子视图。
true:拦截,自身的onTouchEvent方法进行消费。
③onTouchEvent() :
return false则不消费事件,会被传递给父视图的onTouchEvent方法进行处理。
return true 消费事件。
view的onTouchEvent,OnClickListerner和OnTouchListener的onTouch方法 三者优先级
dispatchTouchEvent(onTouchListener)->onInterceptTouchEvent(onTouchEvent)->onClickListener
①dispatchTouchEvent中先执行mOnTouchListener.onTouch(),onTouchListener的onTouch()优先级比onTouchEvent高,会先触发。
②假如onTouch方法返回false会接着触发onTouchEvent;返回true,onTouchEvent方法则不会被调用。
③onClick事件是在onTouchEvent的MotionEvent.ACTION_UP事件通过performClick() 触发的,onClickListener
执行的前提是执行了onTouchEvent,
且能执行MotionEvent.ACTION_UP
事件。
onTouch 和onTouchEvent 的区别
onTouch方法是View的OnTouchListener接口中定义的方法。 当一个View绑定了OnTouchListener后,当有touch事件触发时,就会调用onTouch方法;onTouchEvent 处理点击事件是在dispatchTouchEvent中掉用
ACTION_CANCEL什么时候触发
①如果在父View中拦截ACTION_UP或ACTION_MOVE,在第一次父视图拦截消息的瞬间,父视图指定子视图不接受后续消息了,同时子视图会收到ACTION_CANCEL事件。
②如果触摸某个控件,但是又不是在这个控件的区域上抬起(移动到别的地方了),就会出现action_cancel。
事件先到DecorView还是先到Window
DecorView -> Activity -> PhoneWindow -> DecorView
当屏幕被触摸,事件从Native层分发Framework层的InputEventReceiver.dispachInputEvent()->ViewRootImpl.WindowInputEventReceiver.dispachInputEvent()->ViewRootImpl中的DecorView.dispatchInputEvent()->Activity.dispatchInputEvent()->window.superDispatchTouchEvent()->DecorView.superDispatchTouchEvent()->ViewGroup.superDispatchTouchEvent()
点击事件被拦截,但是想传到下面的View,如何操作
重写子类方法requestDisallowInterceptTouchEvent(true),就不会执行父类的onInterceptTouchEvent(), 将点击事件传到下面的View,剥夺了父view对除了ACTION_DOWN以外的事件的处理权。
如何解决View的事件冲突
开发中常见的事件冲突有ScrollView与RecyclerView的滑动冲突、RecyclerView内嵌同时滑动同一方向
滑动冲突的实现方法:
外部拦截法:
指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就在ACTION_MOVE中开始拦截事件,重写onInterceptTouchEvent方法,那么后续ACTION_UP也将默认交给父View处理。
内部拦截法:
即父View不拦截任何事件,所有事件都传递给子View,子View根据需要决定是自己消费事件还是给父View处理。
如果子View需要此事件就直接消费,否则就交由父容器进行处理,配合requestDisallowInterceptTouchEvent(false)方法。
在 ViewGroup 中的 onTouchEvent 中消费 ACTION_DOWN 事件,ACTION_UP事件是怎么传递
一个事件序列只能被一个View拦截且消耗。因为一旦一个元素拦截了此事件,那么这个事件序列内的所有事件都会直接交给它处理(即不会再调用这个View的拦截方法去询问它是否要拦截了,而是把剩余的ACTION_MOVE、ACTION_DOWN等事件直接交给它来处理)。
Activity ViewGroup和View都不消费ACTION_DOWN,那么ACTION_UP事件是怎么传递的
ACTION_DOWN:-> Activity.dispatchTouchEvent() -> ViewGroup1.dispatchTouchEvent() -> ViewGroup1.onInterceptTouchEvent() -> view1.dispatchTouchEvent() -> view1.onTouchEvent() -> ViewGroup1.onTouchEvent() -> Activity.onTouchEvent();
ACTION_MOVE > Activity.dispatchTouchEvent() -> Activity.onTouchEvent(); -> 消费
同时对父 View 和子 View 设置点击方法,优先响应哪个
先响应子view,如果先响应父view,那么子view将永远无法响应,父view要优先响应事件,必须先调用 onInterceptTouchEvent 对事件进行拦截,事件就不会再往下传递,直接交给父 view 的 onTouchEvent 处理。