上一次我们创建了TouchView,这次我们创建一个TouchViewGroup
public class TouchViewGroup extends LinearLayout {
public TouchViewGroup(Context context) {
this(context,null);
}
public TouchViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public TouchViewGroup(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
//事件分发
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("AAA","ViewGroup + dispatchTouchEvent-->"+ev.getAction());
return super.dispatchTouchEvent(ev);
}
//事件触摸
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("AAA","ViewGroup + onTouchEvent-->"+event.getAction());
return super.onTouchEvent(event);
}
//事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("AAA","ViewGroup + onInterceptTouchEvent-->"+ev.getAction());
return super.onInterceptTouchEvent(ev);
}
}
接着我们把之前的LinearLayout替换掉,xml布局为这样
<livesun.clientb.TouchViewGroup
android:id="@+id/root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<livesun.clientb.TouchView
android:id="@+id/touch"
android:layout_width="200dp"
android:layout_height="150dp"
android:background="@color/colorAccent"
/>
正常情况下
我们运行点击 打印台输出结果
ViewGroup dispatchTouchEvent DOWN -> ViewGroup onInterceptTouchEvent DOWN -> View dispatchTouchEvent DOWN -> View OnTouchListener DOWN -> View onTouchEvent DOWN -> ViewGroup dispatchTouchEvent MOVE
-> ViewGroup onInterceptTouchEvent MOVE -> View dispatchTouchEvent MOVE -> View OnTouchListener MOVE -> View onTouchEvent MOVE -> ViewGroup dispatchTouchEvent UP
-> ViewGroup onInterceptTouchEvent UP -> View dispatchTouchEvent UP -> View OnTouchListener UP -> View onTouchEvent UP -> onClickListener。
现象2
把setOnClickListener去掉
执行结果
ViewGroup dispatchTouchEvent DOWN -> ViewGroup onInterceptTouchEvent DOWN -> View dispatchTouchEvent DOWN -> View OnTouchListener DOWN -> View onTouchEvent DOWN -> ViewGropu onTouchEvent DOWN
现象3
把setOnClickListener去掉,同时让TouchView的返回值为true。
几乎跟正常现象一样,除了没有走onClickListener。
其实我们不去掉setOnClickListener,结果也一样,
原因view的事件分发已经说清楚了,没有走super方法,onClickListner在onTouchEvent的up方法中调用。
ViewGroup dispatchTouchEvent DOWN -> ViewGroup onInterceptTouchEvent DOWN -> View dispatchTouchEvent DOWN -> View OnTouchListener DOWN -> View onTouchEvent DOWN -> ViewGroup dispatchTouchEvent MOVE
-> ViewGroup onInterceptTouchEvent MOVE -> View dispatchTouchEvent MOVE -> View OnTouchListener MOVE -> View onTouchEvent MOVE -> ViewGroup dispatchTouchEvent UP
-> ViewGroup onInterceptTouchEvent UP -> View dispatchTouchEvent UP -> View OnTouchListener UP -> View onTouchEvent UP
现象4
把一切恢复,让onInterceptTouchEvent返回true
事件全被拦截了
ViewGroup dispatchTouchEvent DOWN->
ViewGroup onInterceptTouchEvent DOWN-> ViewGroup onTouchEvent DOWN
现象5
我们扩大TouchViewGroup设置onTouchListener,并设置背景和点击事件,我们点击TouchView以外的地方,看看TouchViewGroupd的分发流程是否和view的一样
ViewGroup dispatchTouchEvent DOWN -> ViewGroup onInterceptTouchEvent DOWN -> ViewGroup OnTouchListener DOWN -> ViewGroup onTouchEvent DOWN -> ViewGroup dispatchTouchEvent MOVE
-> ViewGroup onInterceptTouchEvent MOVE -> ViewGroup OnTouchListener MOVE -> ViewGroup onTouchEvent MOVE -> ViewGroup dispatchTouchEvent UP
-> ViewGroup onInterceptTouchEvent UP -> ViewGroup OnTouchListener UP -> ViewGroup onTouchEvent UP ->
ViewGroup OnClickListener。
跟view的Touch极其相似,只不过多了一个onInterceptTouchEvent方法。
下面,我们源码分析一波
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//....
//这个值是dispatchTouchEvent的返回值,默认为false。
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
//ACTION_DOWN时进行一些初始化操作
if (actionMasked == MotionEvent.ACTION_DOWN) {
//清除以往的Touch状态并且设置mFirstTouchTarget==null
cancelAndClearTouchTargets(ev);
resetTouchState();
}
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
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;
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;
}
}
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
第一步
有一处关键的地方
//ACTION_DOWN时进行一些初始化操作 进行一系列初始化操作
if (actionMasked == MotionEvent.ACTION_DOWN) {
//清除以往的Touch状态,并且设置mFirstTouchTarget为null
cancelAndClearTouchTargets(ev);
resetTouchState();
}
我们看看cancelAndClearTouchTargets方法
private void cancelAndClearTouchTargets(MotionEvent event) {
// 如果mFirstTouchTarget不为null 就会执行clearTouchTargets方法
if (mFirstTouchTarget != null) {
boolean syntheticEvent = false;
if (event == null) {
final long now = SystemClock.uptimeMillis();
event = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
syntheticEvent = true;
}
for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
resetCancelNextUpFlag(target.child);
//通过递归来调用子控件的dispatchTouchEvent
dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
}
clearTouchTargets();
if (syntheticEvent) {
event.recycle();
}
}
}
// mFirstTouchTarget = null;
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
第二步
// intercepted 默认为false
final boolean intercepted;
如果actionMasked为DOWN事件 或者mFirstTouchTarget不为null
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//是否允许父控件拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//调用了onInterceptTouchEvent方法
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
以上代码,使用变量intercepted来标记ViewGroup是否拦截Touch事件的传递,if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) ,第一次进来时,是down事件,所以会走该方法,注意此处的mFirstTouchTarget为null,但会在下面接着赋值。非常关键的变量。
判断disallowIntercept标志位,而这个标记在ViewGroup中提供了public的设置方法
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
通过在子控件getParent.requestDisallowInterceptTouchEvent(true/false);默认为false,如果false 则调用onInterceptTouchEvent,而onInterceptTouchEvent默认为false,此时,intercepted为false
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
第三步
检查cancel。
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
第四步
boolean变量split,默认是true,是否把事件分发给多个子view,同样提供了方法
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
public void setMotionEventSplittingEnabled(boolean split) {
if (split) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
} else {
mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
}
}
第五步
事件不是ACTION_CANCEL并且ViewGroup的拦截标志位intercepted为false,就会进入下面这段代码
if (!canceled && !intercepted) {
//ACTION_DOWN的特殊处理
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE)
{
final int actionIndex = ev.getActionIndex();
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
反序的for循环,如在frameLayout 或者 RelativeLayout中,最后面的子控件会覆盖在前面的子控件上面。
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
注意看上面代码,如果找到newTouchTarget,就会跳出循环,如果没有,就会执行dispatchTransformedTouchEvent方法。这个方法很重要
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
//当前子View是否在mFirstTouchTarget.next这条target链中
private TouchTarget getTouchTarget(@NonNull View child) {
for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
if (target.child == child) {
return target;
}
}
return null;
}
这里需要注意的是,首先通过反序for循环,循环遍历出获取到焦点的子view,为什么使用反序循环,比如frameLayout 和RelativeLayout中,最后面的子控件会覆盖在前面的子控件上,也最优先获取到焦点和点击事件。
这里说下大概流程,事件从Activity开始,到Window接着又分发给DecorView,DecorView的子控件是一个LinearLayout,接着又分为上下两部分,一个标题栏,一个id为content的FrameLayout,这里面装的就是我们的布局。
点击我们布局中的一个view,此时事件从屏幕开始–>一路从activity开始-到了DecorView的dispatchTouchEvent,走到了反序for循环这里,遍历吧,先判断这个子view LinearLayout是否获取到了焦点(这里它肯定获取了焦点),调用dispatchTransformedTouchEvent方法,cancel为false,child不为null,最终调用了child.dispatchTouchEvent(transformedEvent),把事件分发给了唯一子view LinearLayout。
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
接着,又走了ViewGroup的dispatchTouchEvent的方法,又走到这里的反序for循环,一个标题栏,一个盛放我们内容的FrameLayout,肯定是该FrameLayout获取到焦点,又会调用一次dispatchTransformedTouchEvent方法,cancel为false,child不为null,最终又调用了child.dispatchTouchEvent(transformedEvent),把事件分发给了id为content的FrameLayout,就这么一直分发下去,直到找到我们点击的子view。
如果最终view消费了事件,他的dispatchTouchEvent返回true,其父容器的dispatchTransformedTouchEvent就是为true,走进该代码块,会给mFirstTouchTarget赋值,同时把该view做为target.child加入链表中,alreadyDispatchedToNewTouchTarget=true,跳出循环。
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
mFirstTouchTarget不为null,alreadyDispatchedToNewTouchTarget=true,最后其父容器也返回true,一直这样往回走。
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
}
如果view没消费事件,那就不会走dispatchTransformedTouchEvent这个代码中,mFirstTouchTarget=null
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
}
一直调用父类的dispatchTouchEvent,也就是View的dispatchTouchEvent,最终会调用ViewGroup的onTouchEvent方法,依此类推,如果所有的View都不处理该事件,则会将该事件又传递给Activity,即传递给Activity的onTouchEvent方法。
我们看下dispatchTransformedTouchEvent这个方法,通过这个方法,把Touch事件往下传递给子view,如果没有子控件,会调用super.dispatchTouchEvent(event),也就是View的dispatchTouchEvent方法,接着会调用onTouchEvent方法,这也是为什么在拦截子控件为true,会调用VIewGoup方法。
如果子view不为null,会调用child.dispatchTouchEvent(event),如果该方法返回true,则代表了子view消费了该事件。
此时,给newTouchTarget赋值,alreadyDispatchedToNewTouchTarget=true
//给newTouchTarget赋值;
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
如果,dispatchTransformedTouchEvent为false,即子View的onTouchEvent返回false(即Touch事件未被消费),那么就不满足该if条件,也就无法执行addTouchTarget(),从而导致mFirstTouchTarget为null,接着MOVE 和UP事件就进不来。
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null)
之后的一系列判断无法通过。。这也解释了现象2中,不设置点击事件就不走的情况,因为不设置点击事件,相当于没有消费事件,从而导致dispatchTransformedTouchEvent为false,而现象3中,让view的onTouchEvent返回true,就一切正常,是因为消费了事件,dispatchTransformedTouchEvent为true。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
//以上面代码结合分析,cancle为false,事件为ACTION_DOWN
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
if (newPointerIdBits == 0) {
return false;
}
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
第六步
根据上述代码中,对ACTION_DOWN的特殊处理可以发现:
如果dispatchTransformedTouchEvent返回true 代表消费了事件,此时mFirstTouchTarget!=null
如果dispatchTransformedTouchEvent返回false 代表没消费事件,此时mFirstTouchTarget=null
第七步
ACTION_DOWN 之后的 MOVE和UP 不会走上面的流程,而是直接从这里开始
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
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;
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;
}
}
如果按照上述流程走完,mFirstTouchTarget或者为null 或者不为null。
代码也做了判断,如果为null,说明Touch事件未被消费,找不到消费Touch事件的子控件,或者该子控件的Touch事件被拦截了
第三个参数为null
dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS)
他就会调用super.dispatchTouchEvent(event),然后调用其本身的onTouchEvent方法处理Touch事件。
所以正常现象中,不会走TouchViewGroup的onTouchEvent方法也是因为如此,我们给TouchView设置了点击事件,代表有消费事件。
第三个参数不为null
dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)
不会调用super.dispatchTouchEvent(event),而是调用了child.dispatchTouchEvent(event)方法,走了TouchView的一系列事件。
总结
1、事件分发,会先传递给ViewGroup,然后在通过ViewGroup递归传递到一下一层的view(或者ViewGroup)中,这样一层层传递下来,而我们app中最顶级的View,DecorView,想必派发事件就是由此开始。
2、如果子view消费了事件,那么ViewGroup的onToucheEvent将不会执行,也就是说接收不到任何事件,原因就是在dispatchTransformedTouchEvent方法中。
3、如果设置setOnClickListener,就代表了消费事件,如果不设置setOnClickListener,通常情况下,我们会在View的onTouchEvent中返回true,来消费事件,从而执行MOVE UP DOWN事件
至此,整个View与ViewGroup的Touch事件分析完毕,个人水平有限,具体细节不能说明,但是把握住了整个事件派发的大体流程,对以后写自定义控件,处理事件冲突,相比已经有了非常大的帮助。
View的Touch事件分析 –> https://livesun.github.io/2017/07/27/viewTouchEvent/
上一篇