Android事件分发机制学习笔记
今天刚接触Android的事件分发机制,参考网上的资料,而且又正好买了人生中第一款正版桌面应用——MindNode2。所以就用它来制作了一个Android事件分发的思维导图。
接下来且听我细细道来:
代码是参考网上他人博客的,转载地址:Android事件分发和消费机制
先来看看布局文件:
<?xml version="1.0" encoding="utf-8"?>
<cn.sunzn.tevent.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#468AD7"
android:gravity="center"
android:orientation="vertical" >
<cn.sunzn.tevent.TouchEventChilds
android:id="@+id/childs"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:background="#E1110D"
android:text="@string/hello" />
</cn.sunzn.tevent.TouchEventFather>
接下来看一下Activity:Activity重写了dispathchTouchEvent和onTouchEvent两个方法。Activity是顶层,也是第一个出发事件的。
package cn.sunzn.tevent;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
public class TouchEventActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.w("sunzn", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent event) {
Log.w("sunzn", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
return super.onTouchEvent(event);
}
然后我们自定义View分别是:TouchEventFather、TouchEventChilds。两个类分别继承了LinearLayout
TouchEventFather类是最外层的View
package cn.sunzn.tevent;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class TouchEventFather extends LinearLayout {
public TouchEventFather(Context context) {
super(context);
}
public TouchEventFather(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("sunzn", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("sunzn", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent ev) {
Log.d("sunzn", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onTouchEvent(ev);
}
}
TouchEventChilds是里面的View
package cn.sunzn.tevent;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class TouchEventChilds extends LinearLayout {
public TouchEventChilds(Context context) {
super(context);
}
public TouchEventChilds(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("sunzn", "TouchEventChilds | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.dispatchTouchEvent(ev);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("sunzn", "TouchEventChilds | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onInterceptTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent ev) {
Log.d("sunzn", "TouchEventChilds | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
return super.onTouchEvent(ev);
}
}
接下来就是一个工具类,主要用于方便调试,很简单。
package cn.sunzn.tevent;
import android.view.MotionEvent;
public class TouchEventUtil {
public static String getTouchAction(int actionId) {
String actionName = "Unknow:id=" + actionId;
switch (actionId) {
case MotionEvent.ACTION_DOWN:
actionName = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
actionName = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
actionName = "ACTION_UP";
break;
case MotionEvent.ACTION_CANCEL:
actionName = "ACTION_CANCEL";
break;
case MotionEvent.ACTION_OUTSIDE:
actionName = "ACTION_OUTSIDE";
break;
}
return actionName;
}
}
下面这个就是我制作的Android分发机制的导图,解释下:
我们可以看到每个类中都有dispatchTouchEvent和onInterceptTouchEvent、onTouchEvent。这是因为他们都来自于View,但是只有Activity没有onInterceptTouchEvent是因为其他两个类是继承LinearLayout,而LinearLayout又继承ViewGroup,ViewGroup继承View。而在Activity中没有onInterceptTouchEvent这个接口。
从Activity开始触发分发方法
首先是dispathchTouchEvent判断是否在本类中消费,返回true表示在dispathchTouchEvent中消费,但是为什么会调用onTouchEvent呢?我们来看看源码,感觉被骗了…
下边的就是Activity中的dispathchTouchEvent,看哪个if,如果事件被分发就返回true,但是现在事件没有被任何地方分发,所以进入了onTouchEvent方法中,这个方法有个if,不用管它,注释里有一句说The default implement ation always returns false所以dispathchTouchEvent还是返回了false。
所以在Activity中dispathchTouchEvent不管返回什么,都会进入onTouchEvent这个方法中。
- 在TouchEventFather中的dispathchTouchEvent为true的情况下,表示由自己消费不向下传递。
返回false表示不分发触摸事件,但触摸事件来自TouchEventActivity,便返回上一层的onTouchEvent进行消费。 再来看看onInterceptTouchEvent,返回true表示拦截不会向下传递,先传递给自己的onTouchEvent处理,如果未做处理则继续向上到Activity中的onTouchEvents处理。
如果返回false,表示不进行拦截,继续向下分发事件。好了最后一个TouchEventChilds和上面一样,不需要在多说什么了,只有一点,最下层的View中onInterceptTouchEvent返回什么都一样,因为没有可以拦截的事件了。
还有一点需要说明的是,在onTouchEvent中如果返回false,表示未进行消费,冒泡到上一层直到Activity,如果返回true表示消费事件不会在向上冒泡,super也是一样。
查看大图