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

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

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

布局文件如下:

<test.lee.carlyle.com.myapplication.touch.MyLinear xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal"
    android:id="@+id/linear"

    >

    <test.lee.carlyle.com.myapplication.touch.MYTextView
        android:layout_width="80dp"
        android:layout_height="50dp"
        android:background="@drawable/bg_sl"
        android:layout_margin="10dp"
        android:text="GOOD 0"
        />

    <test.lee.carlyle.com.myapplication.touch.MYTextView
        android:layout_width="40dp"
        android:layout_height="30dp"
        android:background="@drawable/bg_sl"
        android:layout_margin="10dp"
        android:text="GOOD 1"
        />

</test.lee.carlyle.com.myapplication.touch.MyLinear>

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

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

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

2,事件执行流程:

10-28 17:07:25.957 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action down 
    text View 0 action down 
10-28 17:07:25.963 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
    text View 0 action move 
10-28 17:07:26.260 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
    text View 0 action move 
10-28 17:07:26.276 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
10-28 17:07:26.277 20796-20796/test.lee.carlyle.com.myapplication E/motion: text View 0 action move 
10-28 17:07:26.292 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action move 
    text View 0 action move 
10-28 17:07:26.293 20796-20796/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch action up 
10-28 17:07:26.294 20796-20796/test.lee.carlyle.com.myapplication E/motion: text View 0 action up 

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

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

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

看代码:

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

3, 问题扩展:

  • 当在父布局中 dispatchTouchEvent 的时候,直接返回false 会怎样? 为了验证这个问题,我将布局层级修改了一下,在当前布局的外层,再增加了一层LinearLayout
  1. 在dispatch TouchEvent ActionDown 的时候直接返回false: 事件将不会继续从这层的LinearLayout 继续分发。但是 ActionDown事件却一次传递到TextView 上了。导致TextView press state change。但是无法恢复了。由于当前ViewGroup 层不分发事件,因此,事件在当前层的上层,直接交给OnTouchEvent 进行处理
    1. 如果上层(TextView -> parent -> parent)的ViewGroup 的OnTouchEvent down 事件返回了ture : 后续事件将保持这个流程一直传递下来。
    2. 如果上层的ViewGroup 的OnTouchEvent down 事件返回了false:事件将继续交给上层的上层的onTouchEvent来判断。

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

<LinearLayout dispatchTouchDown= true; onTouchDown= true>
  <LinearLayout dispatchTouchDown= false; onTouchDown= false>
     <TextView/>
  </LinearLayout>
</LinearLayout>

日志如下:
10-28 17:48:40.376 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action down 
    MyLinear dispatch 2131165251 action down 
    text View 0 action down 
10-28 17:48:40.378 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear  onTouch -1 action down 
10-28 17:48:40.388 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:40.434 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:40.450 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:41.112 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
    MyLinear  onTouch -1 action move 
10-28 17:48:41.128 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action move 
10-28 17:48:41.129 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear  onTouch -1 action move 
10-28 17:48:41.130 22150-22150/test.lee.carlyle.com.myapplication E/motion: MyLinear dispatch-1 action up 
    MyLinear  onTouch -1 action up 
  1. 在dispatch TouchEvent 非ActionDown 的时候返回false:此时不影响分发流程

       实验如下:

<LinearLayout dispatchTouchDown= true; onTouchDown= true>
  <LinearLayout dispatchTouchDown= true; dispatchTouchMove = false; onTouchDown= false>
     <TextView onCLickListenter = true />
  </LinearLayout>
</LinearLayout>

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

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

        实验如下:

<LinearLayout dispatchTouchDown= true; onTouchDown= true>
  <LinearLayout dispatchTouch= true; onTouchDown= false>
     <TextView onCLickListenter = true ; onTouchDown= true ; onTouchMove = false />
  </LinearLayout>
</LinearLayout>

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

        结论:只有actionDown 事件中返回true 或 fase 会影响视图树的事件传递流程。

  •        当在FrameLayout 中嵌套两个ViewGroup. top 层 down 先返回true , 然后返回false ;  bottom 层,一直返回true . 那么事件传递流程会怎样?

三、源码分析:

    事件传递树是如何构成的?在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()。

猜你喜欢

转载自my.oschina.net/u/592116/blog/2253599