一个view事件分发,面试官6连问直击灵魂,我被虐的体无完肤

紧接着在在后面又会调用了:

这句只有在view/viewgroup的dispatchTouchEvent返回false的时候,才会走这里,所以后面的action_moveaction_up都会走这里,而此时传入的child=null,从上面代码可以看到,直接调用了父类的dispatchTouchEvent方法。所以从这里不难看出在view/viewgroup的dispatchTouchEvent返回false的时候直接调用了父类的dispatchTouchEvent方法,因此只有action_down事件。

面试官:如果我只想有view的拖拽事件,而不想要view的点击事件,让你重写这个view的拖拽怎么设计

其实这道题考察大家对view的dispatchTouchEvent和view的onTouchEvent事件的处理流程,上面已经分析了想要view能执行到view的touch事件,那么必须要求view的dispatchTouchEvent返回true,而dispatchTouchEvent返回true要么是dispatchTouchEvent直接返回true或者view的onTouchEvent返回true。如果从效率上看,直接将dispatchTouchEvent返回true就ok,而不需要再去关心onTouchEvent方法。

viewgroup拦截

关于拦截无非就是拦截或不拦截,而拦截的条件是返回true,不拦截是返回false或返回super.onInterceptTouchEvent,默认的super是返回false的,因此可以用super表示不拦截

viewgroup拦截实际是通过在dispatchTouchEvent方法中,设置intercepted变量,如果在拦截方法里面返回true,那么intercepted为true,如果为true则在action_down的时候mFirstTouchTarget=null,那么此时是直接调用dispatchTransformedTouchEvent传入的child=null,因此将事件交给了super.dispatchTouchEvent,此时把它当成一个view来处理了。

面试官:有个viewgroup,里面有个view,如果view在dispatchTouchView中不分发事件,并且只在action_move中拦截touch事件向下分发,说说viewgroup到view的各个action是如何分发的?

分析

先贴出事例代码:

testView在testViewgroup里面,testViewgroup在action_move的时候拦截(onInterceptTouchEvent在move返回true),testView不进行分发(dispatchTouchEvent返回true) 咋们通过log来看结果:

这里执行到TestViewgroup#dispatchTouchEvent的action_move之后就执行了TestView#dispatchTouchEvent的action_cancel,然后后面执行TestViewgroup#dispatchTouchEvent和TestViewgroup#onTouchEvent的action_move和action_up。 从前面viewgroup的dispatchTouchEvent分析知道,如果viewgroup在action_down中发现有子view的dispatchTouchEvent返回true,则mFirstTouchTarget不为空,紧接着在action_move的时候进行了拦截,则intercepted=true,既然在move过程中确定了intercepted=true,mFirstTouchTarget不为空,则可以看viewgroup.dispatchTouchEvent部分代码:

TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
//alreadyDispatchedToNewTouchTarget=false
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
//由于move过程中intercepted=true,则cancelChild=true
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
//看到了没这里就是触发child的dispatchTouchEvent的action_cancel
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
//由于next=null,因此mFirstTouchTarget=null,所以在action_move刚进来的时候mFirstTouchTarget=null了,
//待会我们通过反射看下该变量
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
复制代码

上面也说明了在action_move进来的时候先是触发了testView#dispatchTouchEvent的action_cancel,紧接着mFirstTouchTarget=null了,由于mFirstTouchTarget是viewgroup类中私有的变量,我们可以通过反射调用该变量看下是否为空:

//反射代码,关于反射可以看我之前的文章java反射整理

接着在testViewgroup#dispatchTouchEvent中获取mFirstTouchTarget属性:

通过上面可以验证刚才move过程中mFirstTouchTarget为空的判断,日志如下:

看到了没,第一次move的时候mFirstTouchTarget还不是null,第二次move的时候就是null了,因此在后续的move和up过程中,只会此处:

看到了没,这里传进去的child=null,根据上面分析可知,当为null的时候,只会触发super.dispatchTouchEvent,所以到了第二次的move之后,只能看到TestViewgroup的action_move和action_up了。

所以针对上面面试官提的问题,大家知道怎么说了吧,还是针对该问题做个小结:

小结

先是down事件会经过viewgroup的dispatchTouchEvent,再到viewgroup的onInterceptTouchEvent,最后到view的dispatchTouchEvent,此时mFirstTouchTarget不为空,紧接着到了move首先到viewgroup的dispatchTouchEvent,再到viewgroup的onInterceptTouchEvent,由于在move过程中拦截了,紧接着走view的dispatchTouchEvent的action_cancel,此时接着把mFirstTouchTarget至为null,因此后续的move和up事件只会走viewgroup的dispatchTouchEvent和onTouchEvent。 画出一张图来给大家看下:

好了,关于这个问题告一段落了,如果分析有问题,大家可以提出疑问。

面试官:里面的view在onTouchEvent中消费,然后拖动手指,直到手指从其他他viewgroup上挪开手指。

其实这个问题在上面分析中已经分析过了,testview的onTouchEvent中消费,所以在action_down中mFirstTouchTarget不为空,因此在action_move和action_up中mFirstTouchTarget还是不为空,所以不管手指是否已经离开了testview,action_move和action_up还是会走testview的dispatchTouchEvent和onTouchEvent。

小结

首先确定action_down过程中mFirstTouchTarget是否为空,如果不为空,所以不管手指是否已经不在testView上了,action_move和action_up还是会在testView的onTouchEvent上进行消费的。

面试官:view的onTouch和onTouchEvent事件的区别

这个问题就没涉及viewgroup到view的事件传递,onTouch指setOnTouchListener的回调方法,它是优先于onTouchEvent事件的,大家可以看下view的dispatchTouchEvent中有如下代码:

我想这个地方不用多说吧,如果onTouch方法返回true,是不会触发onTouchEvent事件的,所以在开篇第二个问题:如果想屏蔽掉view的点击事件,只想要view的拖拽事件,该怎么处理,其实这里完全可以重写setOnTouchListener的onTouch方法,并且onTouch里面返回true就会屏蔽掉onClickListener事件。

面试官:view的onClick事件在什么时候触发的,和onTouch有什么区别

onClick事件是在onTouchEvent消费事件中的action_up触发的,onTouch是在dispatchTouchEvent中触发的,所以onTouch要先于onClick事件,我们也可以通过onTouch返回true来屏蔽掉onClick事件。

好了,关于这次我面试中遇到的事件分发主要是上面这几个问题,大家有什么其他的问题,可以在评论区互动。

总结

其实android事件分发核心是在viewgroupdispatchTouchEventaction_down过程中找到mFirstTouchTarget是否为空,通过反序遍历子view的dispatchTouchEvent的方法,如果发现有一个子view的dispatchTouchEvent方法返回true,那么mFirstTouchTarget就不为空,否则为空。如果mFirstTouchTarget不为空,那么action_moveaction_up才会往下传递,如果在action_moveaction_up过程中有viewgroup拦截了事件,则此时先向子view的dispatchTouchEvent传递一个action_cancel,并且将mFirstTouchTarget至为null,所以此时action_moveaction_up只会走viewgroupdispatchTouchEventonTouchEvent;如果mFirstTouchTargetaction_down过程中就已经null的话,则从action_down一直向上层view传递,不会有后续的action_moveaction_up了。

文末

不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊~

小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。上传在我的石墨文档中:Android架构视频+BATJ面试专题PDF+学习笔记请君自取,或者关注我后私信【面试】即可无偿分享

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
*

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值