欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android View事件分发和消费源码简单理解

程序员文章站 2022-04-13 22:49:29
android view事件分发和消费源码简单理解 前言: 开发过程中觉得view事件这块是特别烧脑的,看了好久,才自认为看明白。中间上网查了下singwhatiwan...

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事件的分发机制大家可以在本站搜索相应的文章进行扩展学习!

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!