说明:该内容参考《Android群英传》徐宜生著
首先我们先举个栗子,方便我们后面更好的理解。想像一下生活中的一个常见的场景:假设你所在的公司,有一个总经理,级别最高;他下面有一个部长,级别次之;最底层的就是干活的你,没有级别。那么董事会将事情交给了总经理一项任务,总经理将事情交给了部长,部长又把任务交给了你。而你好不容易将任务完成,然后你就把完成的任务交给了部长,部长觉得还不错,就签字后交给了总经理,总经理觉得还不错,就签了字就给了董事会。那么,这样一个任务也就顺理的完成了。Android事件分发和这个场景也非常类似。
那么下面我们就用代码简单的实现一下这样的一个场景:
一个总经理——MyViewGroupA,最外层的ViewGroup
一个部长——MyViewGroupB,中间层的ViewGroup
干活的你——MyView,最内层的View; 如下图
首先我们自定义ViewGourpA:
public class MyViewGroupA extends FrameLayout {
public static 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 onTouchEvent(MotionEvent event) {
Log.i(TAG, TAG + " onTouchEvent");
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, TAG + " onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, TAG + " dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
}
VIewGroupB和ViewGroupA代码大同小异这里就不在粘贴代码了。
自定义View
public class MyView extends View {
public static String TAG = "MyView";
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureSpec(widthMeasureSpec), measureSpec(heightMeasureSpec));
}
/*
* specMode总共有三个值
* EXACTLY:即精确值模式,当组件指定layout_height或layout_width的值时,系统默认是的模式
* AT_MOST:即最大值模式,当layout_width或layout_height使用wrap_content时,控件的大小随内容的变化而变化,但最大值不能超过父控件的大小
* UNSPECIFIED:view想多大就多大,一般在绘制自定义view时才使用
* 一般情况下View类只支持EXACTLY模式。
* */
private int measureSpec(int measureSpec) {
int result = 200;//当控件的layout_height或layout_width为wrap_content时显示的默认的大小
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(specSize, result);
}
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, TAG + " onTouchEvent");
return super.onTouchEvent(event);
}
}
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mvp.handleevent.MainActivity">
<com.example.mvp.handleevent.MyViewGroupA
android:layout_width="368dp"
android:layout_height="495dp"
android:background="#000000">
<com.example.mvp.handleevent.MyViewGroupB
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#ffffff">
<com.example.mvp.handleevent.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffff00">
</com.example.mvp.handleevent.MyView>
</com.example.mvp.handleevent.MyViewGroupB>
</com.example.mvp.handleevent.MyViewGroupA>
</RelativeLayout>
代码很简单,这里就不多做解释;
点击View后的Log如下所示:
可以看见,正常情况下,事件的传递顺序是:
总经理(MyViewGroupA)——>部长(MyViewGroupB)——>你(View),事件在传递的时候执行dispatchTouchEvent()方法,再执行OnInterceptTouchEvent()方法。
事件的处理顺序是:
你——>部长——>总经理。
事件传递的返回值非常容易理解:true,拦截,不继续传递;反之;
事件处理也类似:true,处理了,上一级就不用审核了;false,则给上级处理;
初始的情况,返回值都是false。
有需要源码的同学欢迎访问 https://github.com/downdodoing/HandleEvent.git