事件相关的三个事件方法
dispatcherEvent 负责分发事件的, 函数主要作用是来决定当前的事件是交由自己消费处理,还是交由子控件处理。
返回true,表示事件不再向下分发,事件在这次分发中消费掉。
返回false表示该组件不参与事件分发,事件交由上层组件的onTouchEvent处理。
返回值为super.dispatcherEvent时,表示事件将向下分发。
onInterceptTouchEvent 决定是否要拦截这个事件。
返回true,则事件不再向下传递,交由组件的onTouchEvent处理。
返回false,则事件继续向下传递。
onTouchEvent 决定事件是否继续向上冒泡。
返回true则事件不再向上冒泡,事件被直接消费掉。
返回false则事件向上冒泡。
调用顺序
dispatcherEvent -> onInterceptTouchEvent - > onTouchEvent
下面用一实例来证明上面的结论。
我写了三个简单的控件继承已有的控件,并实现其中事件响应的方法。
public class MyViewGroup1 extends RelativeLayout{
//...省略构造方法
@Override
public boolean onTouchEvent(MotionEvent event) {
String msg = "MyViewGroup1 -> onTouchEvent";
Log.d(TAG, msg);
return super.onTouchEvent(event);
}
/** * 返回false 拦截事件 返回true 放 */
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
String msg = "MyViewGroup1 -> onInterceptTouchEvent";
Log.d(TAG, msg);
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
isFirst = false;
String msg = "MyViewGroup1 -> dispatchTouchEvent";
Log.d(TAG, msg);
return super.dispatchTouchEvent(ev);
}
}
}
-------
public class MyViewGroup2 extends RelativeLayout{
//...省略构```
/** * 返回false 拦截事件 * 返回true 放 */
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
String msg = "MyViewGroup2 -> onInterceptTouchEvent";
Log.d(TAG, msg);
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
String msg = "MyViewGroup2 -> dispatchTouchEvent";
Log.d(TAG, msg);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
String msg = "MyViewGroup2 -> onTouchEvent";
Log.d(TAG, msg);
return super.dispatchTouchEvent(ev);
}
}
}--------------
public class MyView1 extends TextView {
private static final String TAG = "MyViewGroup2";
private boolean isFirst = true;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
String msg = "MyView1 -> dispatchTouchEvent";
Log.d(TAG, msg);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isFirst) {
isFirst = false;
String msg = "MyView1 -> onTouchEvent";
Log.d(TAG, msg);
return super.dispatchTouchEvent(ev);
} else {
return true;
}
}
}
//为了让防止点击时响应多次点击,加入以上代码中的isFirst判断
----------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<wei.jiang.selfview.eventtest.MyViewGroup1
android:id="@+id/outGroup"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<wei.jiang.selfview.eventtest.MyViewGroup2
android:id="@+id/inGroup"
android:layout_centerInParent="true"
android:background="@android:color/darker_gray"
android:layout_width="200px"
android:layout_height="200px" >
<wei.jiang.selfview.eventtest.MyView1
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_centerInParent="true"
android:text="内容" >
</wei.jiang.selfview.eventtest.MyView1>
</wei.jiang.selfview.eventtest.MyViewGroup2>
</wei.jiang.selfview.eventtest.MyViewGroup1>
</LinearLayout>
这是我们画出的组件,其中文字部分是MyView1, 灰色部分是MyViewGroup2, 白色部分是MyViewGroup1,
我们在不修改任务事件方法的情况下,日志如下。
图1
可以看出只有最里层的子控件响应了onTouchEvent.
首先来实验dispatchTouch方法,这里我们改改MyViewGroup2即中间控件组的dispatchTouch方法的返回值,结果如下:
return true ->
图2
可以 看到事件到了viewGroup2的dispatchTouchEvent方法时就直接被消耗掉。
return false ->
图3
事件又返回到上层父控件的onTouchEvent的方法响应。这里父控件响应了这次的touch事件,并。
这里可以得到结论。
dispatchTouchEvent
返回true,表示事件不再向下分发,事件在这次分发中消费掉。
返回false表示该组件不参与事件分发,事件交由上层组件的onTouchEvent处理。
返回值为super.dispatcherEvent时,表示事件将向下分发。
现在来看onInterceptTouchEvent, 查看父类源码可发现父类直接返回了false,所以这里修改成true看结果。
return true ->
图4
这里可以看出事件都是在viewGroup2的onTouchEvent中被响应,但事件并没有被消耗掉,事件会不断的在MyViewGroup2中传递。
最后我们来修改onTouchEvent的返回值,
return true ->
图5
可以看出事件到了MyViewGroup2被正常消耗掉。事件被拦截了一次
return false ->
图6
事件被MyViewGroup2响应,并且冒泡给了MyViewGroup2的onTouchEvent方法响应。
做到这里,上面的关于三个事件分发方法的结论已经得到了验证。但还有一个问题就是事件的消耗的问题,
做到这里,上面的关于三个事件分发方法的结论已经得到了验证。但还有一个问题就是事件的消耗的问题,
1. 当dispatchTouch方法返回true或者onTouchEvent方法返回true时 如图2,事件被耗掉,日志中会有两次响应的过程,
但只有第一次响应了onInterceptTouchEvent方法,如图5,查看源码可以发现onInterceptTouchEvent对拦截做过判断,
说明这是两次事件过程,个人理解是按下与松开的事件。
2. 当事件返回到上层父控件响应后,事件响应过程只有一次,如图3,6, 说明事件被冒泡到上层后被上层消耗掉,之后相同事件将会直接被上层响应。(侍论证)
3,当事件没有被消耗掉时,如图4,事件会在子控件中不断的响应,这里应该是由onTouchEvent方法内部不断的循环调用造成。