之前学一个新的知识,在学的时候总感觉自己会了,但是当用的时候还是懵B,然后继续去找博客学习,然后想,这也不是个事吧,既然学的时候感觉自己会了,为什么不在当时记录下来呢,就算过后自己忘记了,翻翻自己的博客,根据自己原来的理解去过一遍,那样不是比去看别人的博客更加容易重拾知识吗,而且将学习心得记录下来本身也是对知识点的一种巩固,然后。。。就有了这篇文章
这篇文章并没有涉及到源码层次,只是自己在学习的过程中总结的一些经验,所以如果你对android中的事件分发机制还有些许疑惑的时候可以来看看本篇文章,希望对您有所帮助
首先,我们需要知道在事件机制中有三个方法:
(1)dispatchTouchEvent 对touch事件进行分发的方法
(2)onInterceptTouchEvent 对touch事件进行拦截的方法
(3)onTouchEvent 对touch事件进行处理的方法
下面我们来用一个Demo来跟踪一下这三个方法的调用顺序
布局代码如下所示
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.touchdemo.MainActivity">
<com.touchdemo.ViewGroupA
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.touchdemo.ViewGroupB
android:background="@android:color/holo_blue_dark"
android:layout_width="300dp"
android:layout_height="300dp">
<com.touchdemo.ViewC
android:background="@android:color/holo_green_dark"
android:layout_width="100dp"
android:layout_height="100dp"
android:id="@+id/click"
/>
</com.touchdemo.ViewGroupB>
</com.touchdemo.ViewGroupA>
</LinearLayout>
布局图
我们在ViewGroupA、ViewGroupB、ViewC中都重写了dispatchTouchEvent()、onTouchEvent()方法,在ViewGroupA、ViewGroupB中还重写了onInterceptTouchEvent()方法,因为View中是没有onInterceptTouchEvent()方法的所以ViewC中是没有该方法的,并且在每个重写的方法中添加Log打印的代码片段
ViewGroupA和ViewGroupB的代码基本一样,如下
public class ViewGroupA extends LinearLayout {
String TAG = "ViewGroupA";
public ViewGroupA(Context context) {
super(context);
}
public ViewGroupA(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "onTouchEvent");
return super.onTouchEvent(event);
}
}
ViewC的代码如下:
public class ViewC extends TextView {
String TAG = "ViewC";
public ViewC(Context context) {
super(context);
}
public ViewC(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ViewC(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "dispatchTouchEvent");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "onTouchEvent");
return super.onTouchEvent(event);
}
}
我们在不做任何处理的情况下运行代码,输出Log如下
来张配图
从图中我们可以知道:事件派发顺序是从上到下,事件的消费则是从下往上。
可能很多人看到这不明白了,什么事件派发,什么事件消费?呃呃….
还是用代码说话,我们在ViewGroupA的dispatchTouchEvent方法中返回true
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent");
return true;
}
运行代码,Log如下
E/ViewGroupA: dispatchTouchEvent
是的,点击屏幕后就调用了dispatchTouchEvent方法,后面的方法完全没有反应,这说明在dispatchTouchEvent方法中返回true后,直接将事件给掐断(找不到什么好的词来形容),事件到此为止
然后将代码还原后,我们在ViewGroupA的onInterceptTouchEvent方法中返回true
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "onInterceptTouchEvent");
return true;
}
运行代码,Log如下
E/ViewGroupA: dispatchTouchEvent
E/ViewGroupA: onInterceptTouchEvent
E/ViewGroupA: onTouchEvent
看到这里有些人也许明白了,dispatchTouchEvent方法是掐断事件,而onInterceptTouchEvent方法只是将事件拦截下来传到了自己的onTouchEvent方法中
我们再将代码还原,然后再ViewC的onTouchEvent方法中反回true
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "onTouchEvent");
return true;
}
运行代码,Log如下
E/ViewGroupA: dispatchTouchEvent
E/ViewGroupA: onInterceptTouchEvent
E/ViewGroupB: dispatchTouchEvent
E/ViewGroupB: onInterceptTouchEvent
E/ViewC: dispatchTouchEvent
E/ViewC: onTouchEvent
onTouchEvent方法代表事件的消费,如果我们返回true则表示我们需要消费此事件,当然就不会再往上传递啦。
从上面几个代码的实现效果来看,如果我们在任何方法中返回了true,那么事件将不会再向下一级传递,只是在onInterceptTouchEvent方法中返回true会让事件向自己或者像自己的父级传递。