Android View事件分发和消费源码简单理解
android view事件分发和消费源码简单理解
前言:
开发过程中觉得view事件这块是特别烧脑的,看了好久,才自认为看明白。中间上网查了下singwhatiwanna粉丝的读书笔记,有种茅塞顿开的感觉。
很重要的学习方法:化繁为简,只抓重点。
源码一坨,不要指望每一行代码都看懂。首先是没必要,其次大量非关键代码会让你模糊真正重要的部分。
以下也只是学姐的学习成果,各位同学要想理解深刻,还需要自己亲自去看源码。
2.源码分析
由于源码实在太长,而且也不容易看懂,学姐这里就不贴出来了,因为没必要。
以下是学姐简化版源码。
(1)viewgroup.dispatchtouchevent(event)
boolean dispatchtouchevent(motionevent event) { int action = event.getaction(); //判断viewgroup是否拦截touch事件。当为action_down或者找到能够接收touch事件的子view 时,由onintercepttouchevent(event)决定是否拦截。其他情况,即action_move/action_up且 没找到能够接收touch事件的子view时,直接拦截。 boolean intercepted; if (action == motionevent.action_down || mfirsttouchtarget != null) { intercepted = onintercepttouchevent(event); } else { intercepted = true; } //如果viewgroup不拦截touch事件。在action_down时遍历所有子view,查找能够接收touch事件的 子view。如果找到则设置mfirsttouchtarget,并跳出循环。 boolean alreadydispatchedtonewtouchtarget = false; if (!intercepted) { if (action == motionevent.action_down) { for (int i = childrencount - 1; i >= 0; i--) { if (!canviewreceivepointerevents(child) || !istransformedtouchpointinview(x, y, child, null)) { continue; } if (dispatchtransformedtouchevent(event, child)) { //找到mfirsttouchtarget newtouchtarget = addtouchtarget(child); alreadydispatchedtonewtouchtarget = true; break; } } } } //事件下发及消费。如果没找到能够接收touch事件的子view,则由viewgroup自己处理及消费。 如果找到能够接收touch事件的子view,则由子view递归处理touch事件及消费。 boolean handled = false; if (mfirsttouchtarget == null) { handled = dispatchtransformedtouchevent(event, null); } else { if (alreadydispatchedtonewtouchtarget) { handled = true; } else { while (touchtarget) { handled = dispatchtransformedtouchevent(event, child); } } } return handled; } //viewgroup事件下发。如果无接收touch事件的子view,则由viewgroup的父类(即view)下发touch事件 如果child非空,则交由子view下发touch事件,子view可以是viewgroup或view。 boolean dispatchtransformedtouchevent(motionevent event, view child) { boolean handled; if (child == null) { handled = super.dispatchtouchevent(event); } else { handled = child.dispatchtouchevent(event); } return handled; }
(2)view.dispatchtouchevent(event)
//view的touch事件分发。当外部设置了montouchlistener时,先交由montouchlistener.ontouch(event)消费。 若未消费,则交给view的ontouchevent(event)消费。ontouchevent的实现是,如果设置了monclicklistener, 则执行monclicklistener.onclick()点击事件。返回值为true,表示消费,否则未消费。 boolean dispatchtouchevent(motionevent event) { boolean result = false; if (montouchlistener != null && montouchlistener.ontouch(this, event)) { result = true; } if (!result && ontouchevent(event)) { result = true; } return result; } boolean ontouchevent(motionevent event) { performclick(); }
3.总结
总结下viewgroup的事件分发及消费过程:
整个过程包括3个部分:判断是否拦截 -> 查找接收touch事件的子view -> 事件下发及消费
判断是否拦截:
(1) action_down 或者 非action_down且找到接收touch事件的子view时,由onintercepttouchevent(event)决定是否拦截
(2) 非action_down,且未找到接收touch事件的子view时,标明需要拦截touch事件
这里解释下,影响viewgroup是否能拦截touch事件有2个因素:是否 找到了接收touch事件的子view 和 onintercepttouchevent(event). 而查找接收touch事件的子view这一过程只需要在action_down的时候确定好就行。如果action_down的时候没找到,那么action_move和action_up肯定也找不到,因此touch事件直接被viewgroup拦截。如果找到了接收touch事件的子view,那么action_move和action_up情况下还是要检查下viewgroup的onintercepttouchevent(event),看下是否拦截。
查找接收touch事件的子view:
(1) 两种情况下查找:action_down且viewgroup不拦截的情况下。
(2) 查找方法:遍历所有子view,如果touch事件的xy坐标在该viewgroup的某个子view范围内,则针对该子view执行递归分发touch事件操作,如果找到有子view处理touch事件(return true),则跳出循环。
这里解释下查找条件。查找接收touch事件的子view,显然只需要action_down情况下即可,没必要action_move和action_up都检查,否则重复操作。如果viewgroup都已经拦截了,显然不需要再去考虑子view怎么样了。
事件下发及消费:
(1)两种情况:viewgroup下发及消费 或者 viewgroup的子view下发及消费
(2)如果经过以上两步,没找到接收touch事件的子view,那么由viewgroup进行下发及消费,下发及调用流程是:viewgroup.dispatchtouchevent -> view.dispatchtouchevent -> montouchlistener.ontouch -> ontouchevent -> onclick
(3)如果找到接收touch事件的子view,则针对该子view执行touch事件递归下发及消费的操作
补充:
(1) 源码中,mfirsttouchevent表示接收touch事件的子view
(2) 步骤2和3,都有执行dispatchtransformedtouchevent(event, child)的操作,步骤2中只是为了查找接收touch事件的子view,步骤3主要目的是进行事件分发及消费。如果步骤2中针对某个子view已经执行了该方法,则步骤3中不再重复执行。个人理解,不知道是否有误。
4.结论
(1) 回调方法
viewgroup:dispatchtouchevent -> onintercepttouchevent -> ontouchevent
view: dispatchtouchevent -> ontouch
(2) 调用顺序
action执行顺序:action_down -> action_move -> action_up
viewgroup: dispatchtouchevent -> onintercepttouchevent -> ontouchevent()
view: dispatchtouchevent -> ontouchevent
事件分发传递顺序: parent view -> child view
viewgroup1.dispatchtouchevent -> viewgroup2.dispatchtouchevent
-> view3.dispatchtouchevent
(紧跟着是view3.ontouchevent)
事件消费传递顺序:child view -> parent view
view3.ontouchevent -> viewgroup2.ontouchevent
-> viewgroup1.ontouchevent
个人理解这种传递顺序,是由dispatchtransformedtouchevent引起的,这里就是递归调用,整个事件的入口就是viewgroup.dispatchtouchevent.
以上就是android view事件分发和消费源码的文章分享,关于android view事件的分发机制大家可以在本站搜索相应的文章进行扩展学习!
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
上一篇: python实现批量解析邮件并下载附件