Android Activity和ViewGroup中的dispatchTouchEvent机制浅析

本文先引出Activity和ViewGroup中的Touch分发机制,之后我们会再去研究底层窗口window怎么样去处理和收集消息派发给

Activity的过程和原理(会涉及到Framework层的WindowManager)。这篇博客作为研究WindowManager的一个预热吧。

下面我们来看看,Activity和ViewGroup中的touch分发消息的流程。

我们大家都知道,一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE...->ACTION_MOVE

->ACTION_UP.当只有Activity的情况时:先摆上我的测试源码:

public class TestTouch extends Activity{
private final String TAG = "TestTouch"; 
   /** Called when the activity is first created. */ 
   @Override 
   public void onCreate(Bundle savedInstanceState) { 
       super.onCreate(savedInstanceState); 
   } 
  @Override 
  public boolean dispatchTouchEvent(MotionEvent ev){
int action = ev.getAction(); 
       switch (action) { 
       case MotionEvent.ACTION_DOWN: 
           
          Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN"); 
           
          break; 
       case MotionEvent.ACTION_MOVE: 
           
         Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE"); 
           
         break; 
       case MotionEvent.ACTION_UP: 
           
        Log.d(TAG, "dispatchTouchEvent action:ACTION_UP"); 
           
        break; 
       case MotionEvent.ACTION_CANCEL: 
           
       Log.d(TAG, "onTouchEvent action:ACTION_CANCEL"); 
           
       break; 
       } 

 return super.dispatchTouchEvent(ev); 
  }
  @Override
  public void onUserInteraction() {
  Log.d(TAG, "---onUserInteraction-----"); 
   }
  @Override 
   public boolean onTouchEvent(MotionEvent ev) { 
       int action = ev.getAction(); 
       switch (action) { 
       case MotionEvent.ACTION_DOWN: 
           
         Log.d(TAG, "---onTouchEvent action:ACTION_DOWN"); 
           
         break; 
       case MotionEvent.ACTION_MOVE: 
           
        Log.d(TAG, "---onTouchEvent action:ACTION_MOVE"); 
           
        break; 
       case MotionEvent.ACTION_UP: 
           
       Log.d(TAG, "---onTouchEvent action:ACTION_UP"); 
           
       break; 
       case MotionEvent.ACTION_CANCEL: 
           
      Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL"); 
           
      break; 
       } 
       return false; 
   } 
}

Touch事件分发流程很简单:首先调用dispatchTouchEvent,接着调用onUserInteraction,再次就是onTouchEvent。

如果点击的话(会包括ACITON_MOVE,ACTION_UP),会继续调用dispatchTouchEvent,和onTouchEvent.

下面的log信息:


当ACTION_UP事件时不会再出发onUserInteraction

接着来看下,Activity包含一个ViewGroup,而且这个ViewGroup又包含了一个子View,我们来分析下这种情况下的

Touch消息分发过程。首先介绍下基本方法,Android中每个ViewGroup子类都具有下面三个和TouchEvent处理密切相关的方法。

1)public boolean dispatchTouchEvent(MotionEvent ev)  这个方法用来分发TouchEvent

2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent

3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

当touch事件发生时,首先Activity将事件分发到最顶层的View,会到达他的dispatchTouchEvent,由dispatchTouchEvent

进行分发,如果dispatchTouchEvent返回的结果是true,则交给这个View的onTouchEvent处理,如果返回false,则交给

View的onInterceptTouchEvent方法来决定是否拦截这个事件,如果onInterceptTouchEvent返回true,则表示拦截成功,

则交给View的onTouchEvent处理,如果返回false,则表示将事件传递到子View的,由子View的dispatchTouchEvent

进行消息的再分发。如果当传到某一层的子View的onTouchEvent,结果返回的是false,那么这个touchEvent会从这个

View往上传,并且都是onTouchEvent接收。如果传递到最顶层的onTouchEvent也返回false的话,这个事件就作废,

进行下次的事件分发。下面我们来看下我的测试代码,详细分析下。

配置文件touch_test:

<?xml version="1.0" encoding="utf-8"?>

<com.example.test.TouchLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:gravity="center" > 
       <com.example.test.MyTextView 
            android:layout_width="200dp" 
            android:layout_height="200dp" 
            android:id="@+id/tv" 
            android:text="@string/zzzzzz"
            android:textSize="40sp" 
            android:textStyle="bold" 
            android:background="#FFFFFF" 
            android:textColor="#0000FF"/> 
</com.example.test.TouchLinearLayout>

就是一个layout里面加了一个textview。

下面是我的Activity:

public class TestTouch extends Activity{
private final String TAG = "TestTouch"; 
   /** Called when the activity is first created. */ 
   @Override 
   public void onCreate(Bundle savedInstanceState) { 
       super.onCreate(savedInstanceState); 
       setContentView(R.layout.touch_test); 
   } 
}

自定义一个Layout:

public class TouchLinearLayout extends LinearLayout { 

private final String TAG = "MyLinearLayout"; 

public TouchLinearLayout(Context context, AttributeSet attrs) { 
        super(context, attrs); 
       } 
 
    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
        int action = ev.getAction(); 
        switch (action) { 
        case MotionEvent.ACTION_DOWN: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN"); 
            break; 
        case MotionEvent.ACTION_MOVE: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE"); 
            break; 
        case MotionEvent.ACTION_UP: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_UP"); 
            break; 
        case MotionEvent.ACTION_CANCEL: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_CANCEL"); 
            break; 
        } 
        return super.dispatchTouchEvent(ev); 
    } 
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
        int action = ev.getAction(); 
        switch (action) { 
        case MotionEvent.ACTION_DOWN: 
            Log.d(TAG, "onInterceptTouchEvent action:ACTION_DOWN"); 
            break; 
        case MotionEvent.ACTION_MOVE: 
            Log.d(TAG, "onInterceptTouchEvent action:ACTION_MOVE"); 
            break; 
        case MotionEvent.ACTION_UP: 
            Log.d(TAG, "onInterceptTouchEvent action:ACTION_UP"); 
            break; 
        case MotionEvent.ACTION_CANCEL: 
            Log.d(TAG, "onInterceptTouchEvent action:ACTION_CANCEL"); 
            break; 
        } 
        return true; 
 } 
 
    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
        int action = ev.getAction(); 
        switch (action) { 
        case MotionEvent.ACTION_DOWN: 
            Log.d(TAG, "---onTouchEvent action:ACTION_DOWN"); 
            break; 
        case MotionEvent.ACTION_MOVE: 
            Log.d(TAG, "---onTouchEvent action:ACTION_MOVE"); 
            break; 
        case MotionEvent.ACTION_UP: 
            Log.d(TAG, "---onTouchEvent action:ACTION_UP"); 
            break; 
        case MotionEvent.ACTION_CANCEL: 
            Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL"); 
            break; 
        } 
        return false; 
    } 

public class TouchTextView extends TextView{
    private final String TAG = "MyTextView"; 
    public TouchTextView(Context context, AttributeSet attrs) { 
        super(context, attrs); 
    } 
 
    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
        int action = ev.getAction(); 
        switch (action) { 
        case MotionEvent.ACTION_DOWN: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN"); 
            break; 
        case MotionEvent.ACTION_MOVE: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE"); 
            break; 
        case MotionEvent.ACTION_UP: 
            Log.d(TAG, "dispatchTouchEvent action:ACTION_UP"); 
            break; 
        case MotionEvent.ACTION_CANCEL: 
            Log.d(TAG, "onTouchEvent action:ACTION_CANCEL"); 
            break; 
        } 
        return super.dispatchTouchEvent(ev); 
    } 
 
    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
        int action = ev.getAction(); 
        switch (action) { 
        case MotionEvent.ACTION_DOWN: 
            Log.d(TAG, "---onTouchEvent action:ACTION_DOWN"); 
            break; 
        case MotionEvent.ACTION_MOVE: 
            Log.d(TAG, "---onTouchEvent action:ACTION_MOVE"); 
            break; 
        case MotionEvent.ACTION_UP: 
            Log.d(TAG, "---onTouchEvent action:ACTION_UP"); 
            break; 
        case MotionEvent.ACTION_CANCEL: 
            Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL"); 
            break; 
        } 
        return false; 
    } 
}

下面我们根据上面的总结,细分情况,分析事件分发的流程:

1.当TouchLinearLayout的onInterceptTouchEvent的拦截为true,并且TouchLinearLayout的onTouchEvent

为true,我们可以点击屏幕任何地方,看下Log信息:

10-10 15:43:30.847: D/MyLinearLayout(2656): dispatchTouchEvent action:ACTION_DOWN
10-10 15:43:30.847: D/MyLinearLayout(2656): onInterceptTouchEvent action:ACTION_DOWN
10-10 15:43:30.847: D/MyLinearLayout(2656): ---onTouchEvent action:ACTION_DOWN
10-10 15:43:30.937: D/MyLinearLayout(2656): dispatchTouchEvent action:ACTION_MOVE
10-10 15:43:30.937: D/MyLinearLayout(2656): ---onTouchEvent action:ACTION_MOVE
10-10 15:43:30.947: D/MyLinearLayout(2656): dispatchTouchEvent action:ACTION_UP
10-10 15:43:30.947: D/MyLinearLayout(2656): ---onTouchEvent action:ACTION_UP

结论1:TouchLinearLayout处理了所有onTouchEvent。

2.当TouchLinearLayout的onInterceptTouchEvent的拦截为true,并且TouchLinearLayout的onTouchEvent

为false,我们可以点击屏幕任何地方,看下Log信息:

10-10 15:53:31.687: D/MyLinearLayout(3004): dispatchTouchEvent action:ACTION_DOWN
10-10 15:53:31.687: D/MyLinearLayout(3004): onInterceptTouchEvent action:ACTION_DOWN
10-10 15:53:31.687: D/MyLinearLayout(3004): ---onTouchEvent action:ACTION_DOWN
10-10 15:53:31.967: D/MyLinearLayout(3004): dispatchTouchEvent action:ACTION_DOWN
10-10 15:53:31.967: D/MyLinearLayout(3004): onInterceptTouchEvent action:ACTION_DOWN
10-10 15:53:31.967: D/MyLinearLayout(3004): ---onTouchEvent action:ACTION_DOWN
10-10 15:53:32.327: D/MyLinearLayout(3004): dispatchTouchEvent action:ACTION_DOWN
10-10 15:53:32.327: D/MyLinearLayout(3004): onInterceptTouchEvent action:ACTION_DOWN
10-10 15:53:32.337: D/MyLinearLayout(3004): ---onTouchEvent action:ACTION_DOWN

结论2:TouchLinearLayout仅处理了ACTION_DOWN事件,剩下的touchEvent难道凭空消失了么?

其实不是这样的,touchEvent应该是被TouchLinearLayout的外层处理了,我这里就是Activity了。

3.当TouchLinearLayout的onInterceptTouchEvent的拦截为false,并且TouchLinearLayout的onTouchEvent

为true,而且TouchTextView的onTouchEvent也为false。我们要去点击TouchTextView,看下Log信息:

10-10 15:24:34.487: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_DOWN
10-10 15:24:34.487: D/MyLinearLayout(2501): onInterceptTouchEvent action:ACTION_DOWN
10-10 15:24:34.487: D/MyTextView(2501): dispatchTouchEvent action:ACTION_DOWN
10-10 15:24:34.487: D/MyTextView(2501): ---onTouchEvent action:ACTION_DOWN
10-10 15:24:34.487: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_DOWN
10-10 15:24:34.527: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.527: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.557: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.557: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.567: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.567: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.587: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.587: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.637: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.637: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.657: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.657: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.707: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_MOVE
10-10 15:24:34.707: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_MOVE
10-10 15:24:34.727: D/MyLinearLayout(2501): dispatchTouchEvent action:ACTION_UP
10-10 15:24:34.727: D/MyLinearLayout(2501): ---onTouchEvent action:ACTION_UP

结论3:TouchTextView只处理了ACTION_DOWN事件,TouchLinearLayout处理了所有的touchEvent。

4.当TouchLinearLayout的onInterceptTouchEvent的拦截为false,并且TouchLinearLayout的onTouchEvent

为true,而且TouchTextView的onTouchEvent也为true。我们要去点击TouchTextView,看下Log信息:

10-10 15:13:11.617: D/MyLinearLayout(2354): dispatchTouchEvent action:ACTION_DOWN

10-10 15:13:11.617: D/MyLinearLayout(2354): onInterceptTouchEvent action:ACTION_DOWN
10-10 15:13:11.617: D/MyTextView(2354): dispatchTouchEvent action:ACTION_DOWN
10-10 15:13:11.617: D/MyTextView(2354): ---onTouchEvent action:ACTION_DOWN
10-10 15:13:11.637: D/MyLinearLayout(2354): dispatchTouchEvent action:ACTION_MOVE
10-10 15:13:11.637: D/MyLinearLayout(2354): onInterceptTouchEvent action:ACTION_MOVE
10-10 15:13:11.637: D/MyTextView(2354): dispatchTouchEvent action:ACTION_MOVE
10-10 15:13:11.637: D/MyTextView(2354): ---onTouchEvent action:ACTION_MOVE
10-10 15:13:11.657: D/MyLinearLayout(2354): dispatchTouchEvent action:ACTION_MOVE
10-10 15:13:11.657: D/MyLinearLayout(2354): onInterceptTouchEvent action:ACTION_MOVE
10-10 15:13:11.657: D/MyTextView(2354): dispatchTouchEvent action:ACTION_MOVE
10-10 15:13:11.657: D/MyTextView(2354): ---onTouchEvent action:ACTION_MOVE
10-10 15:13:11.707: D/MyLinearLayout(2354): dispatchTouchEvent action:ACTION_UP
10-10 15:13:11.707: D/MyLinearLayout(2354): onInterceptTouchEvent action:ACTION_UP
10-10 15:13:11.707: D/MyTextView(2354): dispatchTouchEvent action:ACTION_UP
10-10 15:13:11.707: D/MyTextView(2354): ---onTouchEvent action:ACTION_UP

结论4:由上面的Log信息得出结论:消息完全由TouchTextView消费掉。

以上我们总结了4种比较具有代表性的情况,其他情况暂时就不考虑了,相信大家理解了上面的情况,就可以自己想明白

其他情况了,当然也可以用我的代码亲自动手试一下其他情况,看看是不是和自己想的完全一样。

下面我们简单的小结下:Activity的dispatchTouchEvent太简单就不用总结了,只是提一句处理ACTION_UP事件时,

不调用onUserInteractionViewGroup类的Touch事件在onInterceptTouchEvent()和onTouchEvent()

以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。返回值为true表示事件

被正确接收和处理了,返回值为false表示事件没有被处理,将继续传递下去(只是传递方向不一样,

onInterceptTouchEvent()向子View传,而onTouchEvent()向父View传)。当有多层ViewGroup,事件会传递到子ViewGroup

onInterceptTouchEvent,然后再传递到子view的onTouchEvent方法。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值