Android 事件分发,一些被遗漏忽略的细节

一、案例

界面布局 Linear Layout 包裹一个TextView。textView 上设置了按压效果。

问:当手指从TextView 上按下,然后移动手指到TextView 边界以外,发生那些事件?

布局文件如下:

你以为 界面上的效果是这样???:《按压时 TextView 变色。 拖拽过程中,当手指离开textView 后,颜色恢复到normal 状态。》

但是你忽略掉了一点:当没有给TextView 设置事件的时候事件流程状态。

当TextView 没有点击事件的时候,OnTouchEvent down 返回false 。 之后不再相应按压事件。也没有按压效果。

当在TextView 上设置了点击事件后,有了按压效果。

二、事件执行流程:

中间省略了一堆move 日志。 可以看到在手指移出TextView 的范围之后,仍然还将move 事件继续分发到当前touch 焦点的试图上。但是在手指移出textView 视图的时候,按压效果消失了。

之前一直以为这个drawable state change 是因为parent 发出了 state cacel 事件导致的。并以为事件将不再传入。因为觉得合理就没有深究。。。

那么,既然不是 cancel 事件,那么这个 drawable state pressed 状态是怎么消失的呢?

看代码:

原来是View 自己在move 的时候自己处理的。

三、 问题扩展:

当在父布局中 dispatchTouchEvent 的时候,直接返回false 会怎样?为了验证这个问题,我将布局层级修改了一下,在当前布局的外层,再增加了一层LinearLayout

在dispatch TouchEvent ActionDown 的时候直接返回false: 事件将不会继续从这层的LinearLayout 继续分发。但是 ActionDown事件却一次传递到TextView 上了。导致TextView press state change。但是无法恢复了。由于当前ViewGroup 层不分发事件,因此,事件在当前层的上层,直接交给OnTouchEvent 进行处理

如果上层(TextView -> parent -> parent)的ViewGroup 的OnTouchEvent down 事件返回了ture : 后续事件将保持这个流程一直传递下来。

如果上层的ViewGroup 的OnTouchEvent down 事件返回了false:事件将继续交给上层的上层的onTouchEvent来判断。

可见:触摸事件流程是会在视图数中,的确会确定一个处理事件的View路径的。在上层onTouch处理后,是不会再分发给下层的dispatch 去尝试处理的。

在dispatch TouchEvent 非ActionDown 的时候返回false:此时不影响分发流程。

       实验如下:

日志显示,事件都将有TextView 进行处理。外层一致dispatch 到当前层的TextView 上。然后然后交由TextView 来处理。

疑问: 如该在TextView 中,onTouchEvent  down 返回true, 其他的返回false , 又会如何呢?

        实验如下:

实验结果:TextView 的OnTouchEvent 在actionDown 之外返回false。并不影响事件传递流程。

四、源码分析:

    事件传递树是如何构成的?在ViewGroup 中有一个属性。这是一个链式结构。

private TouchTarget mFirstTouchTarget;

确定View tree 事件传递链接的代码在这里:

这个函数在 ViewGroup的dispatchTouchEvent 中的 ActionDown 事件中执行。在此时将确定本层的TouchTarget链表。

其中关键方法:dispatchTransformedTouchEvent.  这个方法确定了是向下层分发(child.dispatch)还是向同层分发(super.dispatch:View.dispatchTouchEvent())。

可见在View.dispatchTouchEvent 中,调用了View.OnTouchEvent.而View.onTouchEvent 的返回值只有在ActionDown 事件时,才能通过是否View 作为TouchTarget 添加到TouchTarget list 中的方式影响到事件分发流程

        一路向上:我们来到Activity dispatchTouchEvent 的代码中看看

跟踪代码发现。getWindow().superDispatchTouchEvent() 实际的处理者是 PhoneWidnow 中的DecoreView(即ViewGroup).dispatchTouchEvent()。

你可能感兴趣的:(Android 事件分发,一些被遗漏忽略的细节)