1、什么是事件分发机制
当用户触摸屏幕时,会产生一个touch事件,这个touch事件(motionEvent)传递到某个具体的view处理的整个过程
用户触摸屏幕会产生一个事件流(ACTION_DOWN -> ACTION_MOVE -> ACTION_UP)
一般来说,view负责处理action_down事件后,会由这个view来处理接下来的事件(注意一般来说)
2、核心方法
-
activity: dispatchTouchEvent / onTouchEvent
-
viewGroup: dispatchTouchEvent / onInterceptTouchEvent / onTouchEvent
-
view: dispatchTouchEvent / onTouchEvent
3、Activity的处理
onUserInteraction()是个空方法,只要接收到down事件,就会执行(用户可重写)。
-
如果getWindow().superDispatchTouchEvent()返回true,则意味着事件被viewGroup或者子view消费掉,则activity.dispatchTouchEvent()也返回true,事件分发结束;
-
若子view或者viewGroup没有对事件进行处理,则getWindow().superDispatchTouchEvent()返回false , 则执行activity.onTouchEvent()
-
无论activity.onTouchEvent() 返回true 还是false, 事件分发都结束。
Activity#dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction(); //空方法,用户可自行实现
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true; //如果被viewGroup/view消费,则事件分发结束
}
return onTouchEvent(ev); //没有任何view消费,事件也分发结束
}
//如果没有任何view消费这个事件,则会执行activity#onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) { //点击区域在边界外,则返回true
finish();
return true;
}
return false;
}
//点击区域在边界外,则返回true,否则返回false
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
final boolean isOutside =
event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
|| event.getAction() == MotionEvent.ACTION_OUTSIDE;
if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
return true;
}
return false;
}
getWindow()#superDispatchTouchEvent()
以前我们说过getWindow返回的是一个PhoneWindow对象,PhoneWindow对象是在activity.attach()里实例化的。
mDecor是通过installDecor()实例化的,是window的根布局。mDecor继承自FrameLayout,FrameLayout继承自ViewGroup。即motionEvent对象由Activity传递到了ViewGroup。(activity - phoneWindow - mDecor - ViewGroup )
PhoneWindow#superDispatchTouchEvent()
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
DecorView#superDispatchTouchEvent(event)
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
4、ViewGroup的处理
ViewGroup每次事件分发时,会先判断disallowIntercept(默认为false) 和 onInterceptTouchEvent (返回false, 不拦截 ) , 决定ViewGroup是否拦截事件,如果不拦截事件,则遍历ViewGroup的子View,调用 view.dispatchTouchEvent( ), 即完成了由ViewGroup传递到View事件。
若点击的是空白处(没有任何View处理此事件),或者手动重写onIntercepTouchEvent()返回为true (拦截),则调用super.DispatchTouchEvent( ),即View.DispatchTouchEvent(ViewGroup继承自View)。由ViewGroup自己处理onTouch事件,则由 ViewGroup 的 onTouch() -> onTouchEvent() -> performClick() -> onClick()
ViewGroup本身是没有onTouchEvent()方法的,所以实际上是调用了父类View.onTouchEvent()
ViewGroup#dispatchTouchEvent()
disallowIntercept 是否禁用事件拦截的功能
- 默认是false,不禁用拦截功能,由onInterceptTouchEvent()来决定是否拦截
- 为true,禁用拦截功能,即不拦截事件
onInterceptTouchEvent()返回true则拦截,false则不拦截
如果设置这两个字段
- disallowIntercept可在子View中通过getParent().requestDisallowInterceptTouchEvent()设置
- onInterceptTouchEvent()用户可重写ViewGroup的onInterceptTouchEvent()
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false; //是否消费事件
if (onFilterTouchEventForSecurity(ev)) {
// 如果是个down事件,则把之前的事件都抛掉不管。
if (actionMasked == MotionEvent.ACTION_DOWN)
//判断是否需要拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
if (!disallowIntercept) {
//只有diallowIntercept为false,且onInterceptTouchEvent为true时才拦截
intercepted = onInterceptTouchEvent(ev);
} else {
//如果diallowIntercept为true,则不拦截
intercepted = false;
}
} else {
// 如果这不是个down事件,也不是上一个的down事件,则意味着拦截(异常情况)
intercepted = true;
}
//判断事件是否已经取消
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
if (!canceled && !intercepted) { // 如果不取消也不拦截,则由子View进行处理
//遍历并分发给子View进行处理
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
break;
}
// handled
}
return handled;
}
ViewGroup#onInterceptTouchEvent()
在 dispatchTouchEvent()内部,判断是否拦截事件MotionEvent()
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
ViewGroup#dispatchTransformedTouchEvent
如果有child,则执行child.disptachTouchEvent(event)
没有child,则执行super.dispatchTouchEvent (ViewGroup继承自View,super也就是View)
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
return handled;
}
5、View的事件处理
onTouch优先于onTouchEvent,更优先于onClick()
View#dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true; //返回true 则事件已被View消费掉,不再往下传递
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
//mTouchListener.onTouch(this, event) 返回true 则事件已被View消费掉,不再往下传递
button.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
return true
}
})
View#onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_CANCEL: //事件取消(非人为)
break;
case MotionEvent.ACTION_MOVE:
break;
}
return true; //若该控件可点击,则一定返回true
}
return false; //若该控件不可点击,则一定返回false
}
View#performClick()
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
li.mOnClickListener.onClick(this); //onTouch() 回调优先于 onClick() 回调
result = true;
} else {
result = false;
}
return result;
}
总结
责任链模式,由Activiy -> ViewGroup -> View
参考