- dispatchTouchEvent
- 18到24行,如果是ACTION_DOWN事件清除状态,里面有一句
mFirstTouchTarget = null;这个变量很重要,后面会用到
- 27到41行,对intercept变量进行处理,这个变量的作用是决定viewgroup要不要对事件进行拦截,为true进行拦截则该group下面的子View不会收到事件。赤裸裸的特权引出来另一个方法onInterceptTouchEvent,此处判断有点多,但是都很必要。一点点看,ACTON_DOWN或者mFirstTouchTarget != null;进入判断,否则intercept=true,disallowIntercept为false且onInterceptTouchEvent(ev)返回true,intercept=true,否则intercept返回false
- 57到159,不执行拦截操作
- 67行,ACTION_DOWN、ACTION_POINT_DOWN、ACTION_HOVER_MOVE三个动作继续执行,其他动作pass掉
- 88行循环遍历,这里注意是从后往前遍历,view排序为屏幕最上层在前面,从后往前遍历的意思就是优先遍历最底层的View,后面会说道首先调用的是window最底层的fragment
- 98到110行首先遍历找到符合 childWithAccessibilityFocus条件的子view进行处理,否则重新开始遍历
- 112行如果 mFirstTouchTarget不为空,采用后插法构造touchTarger队列
- 121行开始递归执行dispatchTransformTouchEvent方法private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)
该方法很长,抓住重点下面一句话:如果传入的child==null,执行super.dispatchTouchEvent,super就是view,否则执行child的dispatchTouchEvent,child可能是viewGroup或者view,viewGroup的话则开始递归调用
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
} - 137行对 mFirstTouchTarget 进行赋值,
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
} - 综上:拦截后进入的循环作用就是在TOUCH_DOWN动作中从底层view到顶层遍历执行dispatchTouchEvent方法,知道遍历结束或者某一dispatchTouchEvent返回true,即出现能消费touchDown事件的view,此时给mFirstTouchTarget 赋值,否则mFirstTouchTarget 为null
- 162到165,mFirstTouchTarget =null,执行
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
注意这里第三个参数传的null。表示调用super.dispatchTouchEvent - 166到196开始另一个循环,遍历touchTarget执行dispatchTransformedTouchEvent方法
- 至此,大体上解析完毕,因为设计递归还有其他逻辑较多,不容易理解下面举两个例子
- 三层View依次重叠放置,其中两个viewGroup,一个view,viewgroup1,viewgroup2,view3假设三个view的touch事件都返回false
- 点击发生在view3上(不考虑Activity层的Touch事件传递)
- 根据上面第一个循环,首先是最底层的view1进入dispatchTouchEvent,然后执行onInterTouchEvent,然后是递归view2依次执行dispatchTouchEvent和onInterTouchEvent,到view3因为是view层了,执行dispatchTouchEvent,onTouch,onTouchEvent,
- 如果view3返回true,代表view3消费了此事件,这时mFirstTouchTarget 的Child为view3,viewgroup2继续向下执行,mFirstTouchTarget 不为null且里面只有一个值,所以
alreadyDispatchedToNewTouchTarget && target == newTouchTarget成立,不会继续执行。同理view1也不会继续执行 - 如果view3返回true,代表view3没有消费此事件,这时mFirstTouchTarget ==null。后续动作都会进入下面的判断里,所以view2和view1的onTouch和onTouchEvent会依次调用
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} - 上面可以判断出来如果view1调用了intercept方法则事件不会传递到上层的viewgroup2和view3,如果view3的dispatchTouchEvent返回true,即view3消费了touch事件,则viewgroup1和viewgroup2不会调用后续的touch动作,即动作不再向上传递
- 此时如果继续执行TOUCH_UP和TOUCH_MOVE动作,由前面的说明可知,这两个动作不会进入第一个循环,直接进入对mFirstTouchTarget == null的判断。如果mFirstTouchTarget == null即前面的view3没有消费掉touch事件,则viewgroup2和viewgroup3依次执行onTouch和onTouchEvent的TOUCH_MOVE事件,否则只有view3执行
- 同理如果ViewGroup2消费了touch事件,则会把view3加入到touchTarger队列中,则viewGroup2和view3两个组件执行后续动作,如果只想让viewGroup2执行后续动作该怎么处理呢,可以把viewgroup2的interceptTouchEvent方法返回true,然后dispatchTouchEvent方法也返回true,即从viewGroup2开始向下拦截向上也拦截
- 点击发生在view3上(不考虑Activity层的Touch事件传递)
- 18到24行,如果是ACTION_DOWN事件清除状态,里面有一句
- 另一个方法onInterceptTouchEvent 比较简单,返回true代表拦截,返回false代表不拦截,执行拦截时,确保ViewGruop的 disallowIntercept 为false,通过myLinearLayout.requestDisallowInterceptTouchEvent(false); 控制,默认为false
- viewgroup没有提供dispatchTouchEvent的实现,使用的view的
- 结论
- viewGroup的onInterceptTouchEvent 方法通过拦截控制事件的向上传递
- view通过将dispatchTouchEvent返回true控制事件不向下传递
- 二者配合完成了安卓事件的冲突处理
- 结论
ViewGroup的touch事件:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
最新推荐文章于 2021-10-14 21:55:13 发布