接着上一篇View的事件分发机制,我接着在说一说ViewGroup的事件分发机制
1.接着来看ViewGroup的事件分发机制,同上一篇,先罗列ViewGroup中主要的方法
ViewGroup的事件分发主要有三个方法
1. dispatchTouchEcent(); 用来分派事件
2. onInterceptTouchEvent ();用来拦截事件
3. onTouchEvent ();用来处理事件
我整理出来的ViewGroup事件分发源码,这里只展示一部分关键代码,其他的可以自行去看源码
private TouchTarget mFirstTouchTarget;
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
if (actionMasked == MotionEvent.ACTION_DOWN){
//清除Target 只要知道mFirstTouchTarget = null;
cancelAndClearTouchTargets(ev);
}
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//相当于调用getParent().requestDisallowInterceptTouchEvent();方法请求父类是否拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
TouchTarget newTouchTarget = null;
if (!canceled && !intercepted) {
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//给mFirstTouchTarget赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
}
}
}
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
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);
}
return handled;
}
}
ViewGroup事件分发流程图
ViewGroup事件分发流程图源码分析
当一个事件传到ViewGroup中的时候,会先看到事件拦截的逻辑,先看下面代码
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;
}
- 首先当一个事件进来的时候,会先判断当前事件是否是down或着mFirstTouchTarget是否为null(当我们第一次进到ViewGroup.dispatchTouchEvent的时候mFirstTouchTarget会为空的),如果当前事件为down或者mFirstTouchTarget为null的时候就会调用ViewGroup.onInterceptTouchEvent方法,接着会将onInterceptTouchEvent的返回进行记录
- 可能有小伙伴会问,disallowIntercept这个值是什么东西,不知道大家有没有用过getParent().requestDisallowInterceptTouchEvent(boolean);方法,当我们给这个方法设置什么,disallowIntercept就会是什么值,所以我们在事件拦截的时候,可以在其子View里面调用该方法进行事件拦截
当走完事件拦截方法之后,程序会拿着onInterceptTouchEvent方法的结果,也就是intercepted的值进行下面的判断
if (!canceled && !intercepted) {}
当intercepted为false也就是不拦截的时候,就会遍历子元素,并将事件向下分发交给子元素进行处理。
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//给mFirstTouchTarget赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
}
}
可以看到当在遍历子孩子的时候会调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)方法,,而在dispatchTransformedTouchEvent方法中我们会发现下面代码,通过下面代码会发现,当child不为空的时候他就会直接调用子元素的dispatchTouchEvent方法,这样事件就交由子元素处理了,从而完成一轮分发。
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
当子View.dispatchTouchEvent返回为true时,就会调用addTouchTarget(child, idBitsToAssign)方法,该方法就是在给mFirstTouchTarget赋值。
当子View.dispatchTouchEvent返回为false时,就不会调用addTouchTarget(child, idBitsToAssign)方法,故mFirstTouchTarget为null
那么mFirstTouchTarget为null时会出现什么情况呢,继续向下看,会看到下面的代码,注意这里的view传的是null,也就是说会调用super.dispatchTouchEvent(event)代码,super.dispatchTouchEvent(event)是什么呢?他就是我们自己的dispatchTouchEcent方法。也就是事件将我们自己去处理
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
到这里ViewGroup的事件分发机制就分析完了
在上一篇View的事件分发机制中我,有提过两个问题
- 重写dispatchTouchEvent、onTouchEvent、设置onTouchEventListener、设置onClickListener但是dispatchTouchEvent返回值不是super而是false的执行效果
- 重写dispatchTouchEvent、onTouchEvent、设置onTouchEventListener、但是不设置onClickListener的执行效果。
其实这两个原理都是相同的,只是运行出来的效果不一样
- 第一个问题运行效果是
ViewGroup.dispatchTouchEvent.down -> ViewGroup.onInterceptTouchEvent.down -> View.dispatchTouchEvent.down-> ViewGroup.onTouchEvent.down
我们可以看到当View. dispatchTouchEvent返回false的时候ViewGroup.dispatchTransformedTouchEvent方法也就返回false故mFirstTouchTarget为null
- 第二个问题运行效果是
ViewGroup.dispatchTouchEvent.down -> ViewGroup.onInterceptTouchEvent.down -> View.dispatchTouchEvent.down-> View.onTouchEventListener.down -> View.onTouchEcent -> ViewGroup.onTouchEvent.down
当View中没有设置onClickListener的时候,也就是说View. dispatchTouchEvent返回false,导致ViewGroup.dispatchTransformedTouchEvent方法也就返回false故mFirstTouchTarget为null
所以上面两个问题最终原因都是因为mFirstTouchTarget为null,当mFirstTouchTarget为null时会调用super.dispatchTouchEvent(event)代码,也就是事件都会交由我们ViewGroup自己去处理,故最后都会调用ViewGroup的onTouchEvent方法。
网友评论