本文先引出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事件时,
不调用onUserInteraction。ViewGroup类的Touch事件在onInterceptTouchEvent()和onTouchEvent()
以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。返回值为true表示事件
被正确接收和处理了,返回值为false表示事件没有被处理,将继续传递下去(只是传递方向不一样,
onInterceptTouchEvent()向子View传,而onTouchEvent()向父View传)。当有多层ViewGroup,事件会传递到子ViewGroup
的onInterceptTouchEvent,然后再传递到子view的onTouchEvent方法。