前言
我们首先要知道,Android 中的事件是用 MotionEvent 对象来表示,那么它的哪些类型呢?
事件类型 | 触发时机 |
ACTION_DOWN | 手指初接触到屏幕时触发 |
ACTION_MOVE | 手指在屏幕上滑动时触发,会多次触发 |
ACTION_UP | 手指离开屏幕时触发 |
ACTION_CANCEL | 事件被上层拦截时触发 |
事件分发,意思就是把事件从一个地方通过某种逻辑分发给其他控件去处理,一般来说,事件都会经过Activity,然后由Activity往下传递到ViewGroup,然后ViewGroup再分发给它的子view,
即 Activity —> ViewGroup —> 子view,
事件给到子ViewGroup后,如果它的子view不需要这个事件,或者它都没有子view,那么这个事件就只能由ViewGroup自己来处理,否则事件就由子view来处理。
那么,事件一定会经过Activity吗?
其实不是的,我们的程序界面的顶层ViewGroup,也就是DecorView
中注册了 Activity 这个callBack,所以程序的主界面接收到事件之后会先交给 Activity。
但是,如果是另外的控件树,如dialog、popupWindow等事件流是不会经过Activity的。只有自己界面的事件才会经Activity。
对于View而言,我们只需要关注事件的处理即可,因为事件传到View,已经相当于是最下层了,不能再继续分发了。
View对事件的处理,是在View.dispatchTouchEvent
方法中处理的。
下面我们就来看看View的事件处理机制,然后再看ViewGroup的事件分发机制。
事件处理
我们在对一个view进行点击事件监听和触摸监听的时候,一般都是这样的写的:
//监听view的点击事件
btn.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
Log.i("testLog", "onClick.")
}
})
//监听view的触摸事件
btn.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, m: MotionEvent?): Boolean {
Log.i("testLog", "onTouch. MotionEvent=${m?.action}")
return false
}
})
用法非常简单,相信大家都已经熟的不能再熟了。但是,我们的重点不在于用法,而是在于原理上,那么问题来了:
问题一:onTouch
方法,它的返回值是Boolean类型的,返回 true 和 false 分别有什么用呢?
问题二:onTouch
和onClick
这两个方法,谁先调用?在哪调用呢?
带着这以上问题,我们直接继续往下来看。
假设我们在当前view的onTouch
方法返回false
,如下:
val btn = findViewById<Button>(R.id.btn)
btn.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
Log.i("testLog", "onClick.")
}
})
btn.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, m: MotionEvent?): Boolean {
Log.i("testLog", "onTouch. MotionEvent=${m?.action}")
return false
}
})
然后我们点击view,查看输出的日志:
I/testLog: onTouch. MotionEvent=0
I/testLog: onTouch. MotionEvent=2
I/testLog: onTouch. MotionEvent=1
I/testLog: onClick.
可以看到,当我们把view的onTouch方法返回false,那么onTouch和onClick都能正常回调了,并且onTouch方法比onClick方法先回调。
然后我们再将view的onTouch
方法返回true
,然后点击view,日志输出如下:
I/testLog: onTouch. MotionEvent=0
I/testLog: onTouch. MotionEvent=2
I/testLog: onTouch. MotionEvent=2
I/testLog: onTouch. MotionEvent=1
可以看到,只有onTouch方法中打印的日志能输出,onClick方法中的日志并没有输出,也就是说,
如果view在它的onTouch方法中返回true,会导致它的onClick方法无法被回调。
那么,为什么view的onTouch方法返回true,会导致它的onClick方法无法回调了呢?
下面我们就开始进入源码环节:
上面我们说过,View的事件处理,是在View.dispatchTouchEvent
方法中处理的,所以我们进入View.java类,看看这个方法的实现:
View:
public boolean dispatchTouchEvent(MotionEvent event) {
// …………………………………………………………………………………………………………………………………………………………………………
boolean result = false;
// …………………………………………………………………………………………………………………………………………………………………………
// 判断事件是否安全,一般正常的事件都是能进入这个if内的
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
/**
* 1、li:只要我们调用了setOnTouchListener,li就不会为null。
* 2、li.mOnTouchListener: 是指setOnTouchListener方法里的OnTouchListener参数,只要正常传入,也不会为null
* 3、view的使能,如果view是可以点击的(enable),那么(mViewFlags & ENABLED_MASK) == ENABLED 就会为 ture
* 4、onTouch方法的执行,并判断它的返回值
* 5、这块if语句,主要取决于onTouch方法的返回值,如果onTouch方法返回true,那么就会将true赋值给result变量
**/
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
/**
* 1、result的值如果为false,才能继续执行onTouchEvent(event)方法
* 2、执行onTouchEvent(event)方法,并判断它的返回值。
* 3、如果result的值为false,且onTouchEvent(event)返回true,那么也会将result赋值为true
**/
if (!result && onTouchEvent(event)) {
result = true;
}
}
// …………………………………………………………………………………………………………………………………………………………………………
return result;
}
View.dispatchTouchEvent
方法中,我只留下了核心部分的代码,我们来分析一下这部分代码,注释里也写得比较明白了:
首先,在我们调用了view的setOnTouchListener(OnTouchListener list)
方法,并且该View是可点击的前提下,
如果OnTouchListener.onTouch方法的返回值为true,那么result变量
就会被赋值为true
。
此时,如果result
被赋值为true
了,就会导致onTouchEvent(event)不会被执行。
反之,如果OnTouchListener.onTouch方法的返回值为false,那么onTouchEvent(event)
才会被执行,
然后,再根据onTouchEvent(event)
的返回值,决定要不要将result
赋值为true。
需要注意的是:
如果onTouchEvent(event)
方法返回true
,那么表示该方法消费了此次事件,如果返回 false
,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。
这时候我们可以大胆猜测一下,刚刚在我们的演示demo上,在onTouch方法返回值设置为true,就会导致onClick方法无法执行。
所以有没有可能onClick的执行是在onTouchEvent(event)
方法中执行的呢?
带着这个猜测,我们进入onTouchEvent方法看看:
public boolean onTouchEvent(MotionEvent event) {
// …………………………………………………………………………………………………………………………………………………………………………
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
// …………………………………………………………………………………………………………………………………………………………………………
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
// …………………………………………………………………………………………………………………………………………………………………………
break;
case MotionEvent.ACTION_CANCEL:
// …………………………………………………………………………………………………………………………………………………………………………
break;
case MotionEvent.ACTION_MOVE:
// …………………………………………………………………………………………………………………………………………………………………………
break;
}
return true;
}
return false;
}
这个方法还挺长的,我们从刚刚的演示代码中也看到了,onClick是在ACTION_UP
事件后才回调的,所以我们直接看到里面处理ACTION_UP的代码吧,可以看到,
这里面构建了一个PerformClick
对象,它其实是个Runnable对象,然后通过post方法执行这个Runnable,我们进入PerformClick
这个类看看:
View.java:
private final class PerformClick implements Runnable {
@Override
public void run() {
recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
// 执行了performClickInternal方法。
performClickInternal();
}
}
private boolean performClickInternal() {
notifyAutofillManagerOnClick();
return performClick();
}
// performClick最终会走到performClick()方法
public boolean performClick() {
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
/**
* 1、li:只要我们调用了setOnClickListener, li就不会为null。
* 2、li.mOnClickListener:指的是setOnClickListener方法里的OnClickListener参数,只要正常传入,也不会为null
**/
if (li != null && li.mOnClickListener != null) {
//点击音效处理
playSoundEffect(SoundEffectConstants.CLICK);
//执行onClick方法
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
源码中可以看到,performClick会走到performClickInternal方法,然后最终会走到performClick()
方法,performClick()
方法中,只要我们调用了setOnClickListener()方法并传入了OnClickListener参数,那么OnClickListener.onClick()就会被执行。
事件处理的总结
最后,我来梳理一下这整个事件从onTouch到onClick的处理流程,
前提:View拿到事件,并执行dispatchTouchEvent(event)
方法。
1、如果设置了View的setOnTouchListener(OnTouchListener list),并且该view是可点击的;
2、执行onTouch(this, event)
方法,如果onTouch方法的返回值是false
;
3、执行onTouchEvent(event)
方法;
4、在onTouchEvent(event)
方法中处理ACTION_UP事件,最终执行了performClick()
方法;
5、在performClick()
方法判断,如果设置了View的setOnClickListener(OnClickListener listen)
方法,则执行onClick(this)
方法。
事件分发
事件产生后,事件是Activity先收到的,所以会先执行Activity的dispatchTouchEvent()
方法:
Activity.java:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
在这个方法中,我们先看到第二个if语句,调用看getWindow().superDispatchTouchEvent()
方法,继续将事件交给PhoneWindow
来处理,
如果getWindow().superDispatchTouchEvent()
返回值为ture,那么整个方法就返回true了,Activity后续就不会再对这个事件进行处理。
如果getWindow().superDispatchTouchEvent()
返回值为false,那么就会执行onTouchEvent(ev)
方法,事件将由Activity进行处理。
PhoneWindow.java:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
DecorView.java:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
然后,PhoneWindow又会将事件交给DecorView来处理。DecorView又会把事件交给它的父类来处理,
DecorView继承自FrameLayout,但是我们走进FrameLayout源码里面看,其实FrameLayout是没有重写dispatchTouchEvent()方法的,所以,,,
执行DecorView的dispatchTouchEvent()方法,实际上就是执行了ViewGroup类的dispatchTouchEvent()
方法。
在View中的dispatchTouchEvent方法,它的作用是用来进行事件处理的,
而在ViewGroup中,重写了父类(View)的dispatchTouchEvent方法,并将dispatchTouchEvent方法改成了事件的分发。
在ViewGroup的dispatchTouchEvent(MotionEvent ev)
方法中,就会进行事件的分发了,我们来看看方法的源码:
ViewGroup.java:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// ..........................................................................................
/**
* 第一部分核心代码
**/
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//如果当前事件是ACTION_DOWN,或者mFirstTouchTarget变量不为null,才能走进来
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法,并将其返回值并赋值给intercepted变量
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
//如果disallowIntercept变量为true,intercepted变量也会为false
intercepted = false;
}
} else {
如果当前事件不是DOWN,并且,mFirstTouchTarget变量为null,则intercepted变量等于true
intercepted = true;
}
// ..........................................................................................
/**
* 第二部分核心代码
**/
if (!canceled && !intercepted) {
............................................................
//如果事件没有被canceled 且 【intercepted值为false】,就把事件分发给子view
//注意:只有事件为ACTION_DOWN的时候,这个if块才有机会执行
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// ..........................................................................................
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
//在这里对子view进行排序,确定事件先给哪个子view处理
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//开始遍历子view
for (int i = childrenCount - 1; i >= 0; i--) {
// ..........................................................................................
//通过dispatchTransformedTouchEvent这个方法,把child方法传进去,对child进行事件的处理
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
............................................................
//在这里会对mFirstTouchTarget这个变量进行赋值,
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
//如果能走进这个if块,那就说明子view把事件处理了,那么直接把整个for循环终止,其他子view就不会再收到这个事件
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
}
// ..........................................................................................
}
/**
* 第三部分核心代码
**/
if (mFirstTouchTarget == null) {
//如果mFirstTouchTarget为null,事件由当前view处理
handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
} else {
//如果mFirstTouchTarget不为null,事件由其子view处理
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//这个循环实际上只会执行一次【多指操作】
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
//处理子view事件
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// ..........................................................................................
return handled;
}
ViewGroup的dispatchTouchEvent(MotionEvent ev)这个方法非常长,我们只看主要核心的流程,我们将它分为以下三个核心部分:
一、第一部分:
/**
* 第一部分核心代码
**/
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//如果当前事件是ACTION_DOWN,或者mFirstTouchTarget变量不为null,才能走进来
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法,并将其返回值并赋值给intercepted变量
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
//如果disallowIntercept变量为true,intercepted变量也会为false
intercepted = false;
}
} else {
//如果当前事件不是DOWN,并且,mFirstTouchTarget变量为null,则intercepted变量等于true
intercepted = true;
}
流程如下:
1、如果当前事件是DOWN,或者mFirstTouchTarget变量不为null
,则走 if 里的代码;否则就直接将intercepted
变量赋值为true,
intercepted变量,代表事件有没有被当前View的ViewGroup拦截,为true表示已被它的ViewGroup拦截
mFirstTouchTarget变量:当事件被子view处理了,那么mFirstTouchTarget就会被赋值,如果事件没有被任何子view处理,那么mFirstTouchTarget的值就默认为null
2、能走到 if 里的代码,说明事件没有被它的ViewGroup拦截。如果disallowIntercept
变量为false
,就执行onInterceptTouchEvent(ev)
方法,并将其返回值并赋值给intercepted
变量;
3、如果disallowIntercept
变量为true
,intercepted
变量直接赋值为false。
onInterceptTouchEvent(ev)
方法是干嘛的呢?
它是用来拦截事件的,如果返回值为true,那么就会把事件拦截下来,然后事件就不能继续分发了,所以onInterceptTouchEvent(ev)
的返回值决定了事件是否继续分发。
上面的变量intercepted
,会第二部分的代码中会被用到。
二、第二部分:
/**
* 第二部分核心代码
**/
if (!canceled && !intercepted) {
// ..........................................................................................
// 如果事件没有被canceled 且 【intercepted值为false】,就把事件分发给子view
// 注意:只有事件为ACTION_DOWN的时候,这个 if 块才有机会执行
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
............................................................
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
// 在这里对子view进行排序,确定事件先给哪个子view处理
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 开始遍历子view
for (int i = childrenCount - 1; i >= 0; i--) {
............................................................
// 通过dispatchTransformedTouchEvent这个方法,把child传进去,对child进行事件的处理
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// ..........................................................................................
// 在这里会对mFirstTouchTarget这个变量进行赋值,
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
//如果能走进这个if块,那就说明子view把事件处理了,那么直接把整个for循环终止,其他子view就不会再收到这个事件
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
}
// ..........................................................................................
}
流程如下:
1、事件是DOWN事件的前提下,如果事件没有被canceled
且 没有被ViewGroup拦截
,即intercepted
值为false
,就把事件分发给子view
2、对子view进行排序,确定事件先给哪个子view处理
3、循环遍历子view,每遍历到一个子view就执行一次dispatchTransformedTouchEvent
方法,并把child(即子View)
传进去,对child
进行事件的处理
4、dispatchTransformedTouchEvent
如果返回true,说明这个子view把事件处理了,则mFirstTouchTarget
将会被赋值,其他子view将不会收到事件,循环结束。
三、第三部分:
/**
* 第三部分核心代码
**/
if (mFirstTouchTarget == null) {
//如果mFirstTouchTarget为null,事件由当前view处理
handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
} else {
//如果mFirstTouchTarget不为null,事件由其子view处理
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//这个循环实际上只会执行一次【多指操作】
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
//处理子view事件
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
流程如下:
1、如果mFirstTouchTarget
为null
,说明该ViewGroup的子View并没有处理掉事件,所以该事件将由当前view自己处理
2、如果mFirstTouchTarget不为null,事件由其子view处理
【mFirstTouchTarget】这个变量,是在第二部分那里赋值的,当事件被子view处理了,那么mFirstTouchTarget
就会被赋值,如果事件没有被任何子view处理,那么mFirstTouchTarget的值就默认为null。另外,不管是事件由当前view处理,还是由子view处理,他们都执行了dispatchTransformedTouchEvent方法,只是入参不同而已。
那么,我们继续进入dispatchTransformedTouchEvent方法看看:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
............................................................
if (child == null) {
//如果child == null,说明是当前的view,直接对当前view进行事件的处理
handled = super.dispatchTouchEvent(transformedEvent);
} else {
//如果child不为null,说明是当前是子view,开始对子view进行事件的处理
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (!child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
/**
* 在这里真正对子view进行事件处理,执行child.dispatchTouchEvent(transformedEvent)方法
* 如果子view把事件处理了,那么这里就会返回true
**/
handled = child.dispatchTouchEvent(transformedEvent);
}
transformedEvent.recycle();
return handled;
}
这个方法对child
参数进行判断,如果child == null
,说明是当前的 view ,直接对当前view进行事件的处理,
即调用super.dispatchTouchEvent(transformedEvent) 方法,并把返回值赋值给handled
变量;
如果child不为null,说明是当前是子view,就对子view进行事件的处理,
即调用child.dispatchTouchEvent(transformedEvent)
方法,并把返回值赋值给handled
变量;
正常情况下,如果view把事件成功处理了,handled变量就会为true。
dispatchTransformedTouchEvent这个方法处理完,就会把handled返回到上一个方法,即dispatchTouchEvent(MotionEvent ev)方法,如果dispatchTouchEvent(MotionEvent ev)方法返回了true,说明事件已经被处理了。
事件分发的总结
那么最后,我们对事件的分发流程做个总结:
1、事件首先传递给Activity
2、Activity将事件传给PhoneWindow处理(如果PhoneWindow不处理,那就自己处理)
3、Activity将事件传给PhoneWindow后,PhoneWindow继续传给DecorView处理
4、DecorView调用ViewGroup的dispatchTouchEvent()对事件进行分发
5、调用onInterceptTouchEvent方法判断是否要进行拦截
6、对intercepted变量进行赋值。如果事件不是DOWN 且 mFirstTouchTarget变量为null,则intercepted变量赋值为true;否则如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法(判断是否要拦截事件),并将其返回值赋值给intercepted变量;如果disallowIntercept变量为true,intercepted变量直接赋值为false。
7、事件是DOWN事件的前提下,如果事件没有被canceled 且 事件没有被ViewGroup拦截 (intercepted值为false),就把事件分发给子view
8、对子view进行排序,确定事件先给哪个子view处理
9、循环遍历子view,每遍历到一个子view就执行一次dispatchTransformedTouchEvent方法,把child传进去,对child进行事件的处理
10、dispatchTransformedTouchEvent如果返回true,说明这个子view把事件处理了,则mFirstTouchTarget将会被赋值,其他子view将不会收到事件,循环结束。
11、如果mFirstTouchTarget为null,事件由当前view自己处理
12、如果mFirstTouchTarget不为null,事件由其子view处理
法判断是否要进行拦截
6、对intercepted变量进行赋值。如果事件不是DOWN 且 mFirstTouchTarget变量为null,则intercepted变量赋值为true;否则如果disallowIntercept变量为false,就执行onInterceptTouchEvent方法(判断是否要拦截事件),并将其返回值赋值给intercepted变量;如果disallowIntercept变量为true,intercepted变量直接赋值为false。
7、事件是DOWN事件的前提下,如果事件没有被canceled 且 事件没有被ViewGroup拦截 (intercepted值为false),就把事件分发给子view
8、对子view进行排序,确定事件先给哪个子view处理
9、循环遍历子view,每遍历到一个子view就执行一次dispatchTransformedTouchEvent方法,把child传进去,对child进行事件的处理
10、dispatchTransformedTouchEvent如果返回true,说明这个子view把事件处理了,则mFirstTouchTarget将会被赋值,其他子view将不会收到事件,循环结束。
11、如果mFirstTouchTarget为null,事件由当前view自己处理
12、如果mFirstTouchTarget不为null,事件由其子view处理