View事件分发机制
什么是分发?分发就是将一些事务交给下属来处理,事件的分发其实包含了:事件的拦截,事件的处理(这样说可能不是太准确)
事件拦截机制中,ViewGroup有onInterceptTouchEvent()
,dispatchTouchEvent()
,onTouchEvent()
三个方法,分别起到事件分发、事件拦截、事件处理的作用
而View中只有dispatchTouchEvent()
,onTouchEvent()
方法。
假设有这样一个Layout: ViewGroupA -> ViewGroupB -> View
当用户点击了这个布局的时候,
首先会从最外层ViewGroupA开始进行判断,首先将事件分发给子View(dispatchTouchEvent()),即子View具有优先处理的权利
ViewGroupA代码如下
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
/**
* Created by feathers on 16-11-13.
*/
public class MyViewGroupA extends LinearLayout {
private final String TAG = "MyViewGroupA";
public MyViewGroupA(Context context) {
super(context);
}
public MyViewGroupA(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "dispatchTouchEvent()");
boolean b = super.dispatchTouchEvent(ev);
Log.i(TAG, "dispatchTouchEvent() return ");
return b;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent()");
boolean b = super.onInterceptTouchEvent(ev);
Log.i(TAG, "onInterceptTouchEvent() return");
return b;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent()");
boolean b = super.onTouchEvent(event);
Log.i(TAG, "onTouchEvent() return");
return b;
}
}
ViewGroupB代码同A类似:
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
/**
* Created by feathers on 16-11-13.
*/
public class MyViewGroupB extends LinearLayout {
private final String TAG = "MyViewGroupB";
public MyViewGroupB(Context context) {
super(context);
}
public MyViewGroupB(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyViewGroupB(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "dispatchTouchEvent()");
boolean b = super.dispatchTouchEvent(ev);
Log.i(TAG, "dispatchTouchEvent() return");
return b;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent()");
boolean b = super.onInterceptTouchEvent(ev);
Log.i(TAG, "onInterceptTouchEvent() return ");
return b;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent()");
boolean b = super.onTouchEvent(event);
return b;
}
}
地位最低的MyButton:
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
/**
* Created by feathers on 16-11-13.
*/
public class MyButton extends Button {
private final String TAG = "MyButton";
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "dispatchTouchEvent()");
boolean b = super.dispatchTouchEvent(event);
Log.i(TAG, "dispatchTouchEvent() return");
return b;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent()");
boolean b = super.onTouchEvent(event);
return b;
}
}
xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.myapplication.MainActivity">
<com.example.myapplication.MyViewGroupA
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.myapplication.MyViewGroupB
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.myapplication.MyButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试"/>
</com.example.myapplication.MyViewGroupB>
</com.example.myapplication.MyViewGroupA>
</RelativeLayout>
点击这个myButton:
输出以下信息:
//**DOWN事件,手按下
//最高一级捕获到动作,将事件分发给子View进行处理
// A开始分发
I/MyViewGroupA: dispatchTouchEvent()
// ViewGroupA是否要拦截事件,返回true代表拦截,则不会分发给子view
I/MyViewGroupA: onInterceptTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent() return
// 如果没有被A拦截,B则会接受到此事件,然后同A相同,分发给子view,看看它们要不要处理
// B开始分发
I/MyViewGroupB: dispatchTouchEvent()
// ViewGroupB是否要拦截这个事件
I/MyViewGroupB: onInterceptTouchEvent()
I/MyViewGroupB: onInterceptTouchEvent() return
// 没有被拦截,则让MyButton获取事件,同样先进性事件分发
I/MyButton: dispatchTouchEvent()
// 因为没有子View,所以不需要onIntercepTouchEvent()取判断是否交给子View处理
// 直接调用onTouchEvent()处理事件,但是有时候子View不想处理,如果不想处理,则onTouchEvent()可以返回false
I/MyButton: onTouchEvent()
// MyButton处理结束后,代表B的事件分发结束
I/MyButton: dispatchTouchEvent() return
// MyButton结束后,B也结束了
I/MyViewGroupB: dispatchTouchEvent() return
// 最后ViewGroupA的事件分发同样也结束了
I/MyViewGroupA: dispatchTouchEvent() return
//**UP事件,手抬起
I/MyViewGroupA: dispatchTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent() return
I/MyViewGroupB: dispatchTouchEvent()
I/MyViewGroupB: onInterceptTouchEvent()
I/MyViewGroupB: onInterceptTouchEvent() return
I/MyButton: dispatchTouchEvent()
I/MyButton: onTouchEvent()
I/MyButton: dispatchTouchEvent() return
I/MyViewGroupB: dispatchTouchEvent() return
I/MyViewGroupA: dispatchTouchEvent() return
当我们将事件拦截在ViewGroupB中会出现什么情况呢?
将ViewGroupB的onInterceptTouchEvent()返回值改为true
再来看log:
// GroupA给子View分发事件
I/MyViewGroupA: dispatchTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent() return
// GroupB给子View分发事件
I/MyViewGroupB: dispatchTouchEvent()
I/MyViewGroupB: onInterceptTouchEvent()
I/MyViewGroupB: onInterceptTouchEvent() return
// 这里发现onInterceptTouchEvent()的返回值为true,即需要拦截事件,此时ViewGroupB发现事件被拦截了,那么它自己就会处理事件
I/MyViewGroupB: onTouchEvent() // 调用onTouchEvent()处理事件
// B分发完成
I/MyViewGroupB: dispatchTouchEvent() return
// B分发完成后发现 A又调用了onTouchEvent()处理了事件,这是因为onTouchEvent()返回了false,即B虽然根据事件进行了处理,但是并没有消耗掉事件,这称为事件回传。
// 前面的MyButton处理事件后B没有调用onTouchEvent()的原因在于MyButton将事件消耗掉了,所以可以理解为事件被吃掉了,没有事件可以回传。
I/MyViewGroupA: onTouchEvent()
I/MyViewGroupA: onTouchEvent() return
// A分发完成
I/MyViewGroupA: dispatchTouchEvent() return
既然dispatchTouchEvent()决定权这么大,那我们将dispatchTouchEvent()置为false会不会就代表这个事件不被处理了呢?
把ViewGroupView的dispatchTouchEvent()置为false:
LOg如下:
I/MyViewGroupA: dispatchTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent()
I/MyViewGroupA: onInterceptTouchEvent() return
I/MyViewGroupB: dispatchTouchEvent()
I/MyViewGroupB: dispatchTouchEvent() return
// A发现B理都不理它,直接自己处理了
I/MyViewGroupA: onTouchEvent()
I/MyViewGroupA: onTouchEvent() return
I/MyViewGroupA: dispatchTouchEvent() return