前言
网上很多关于事件分发的原理、源码分析、流程图解析等等,有些说的比较清楚,有些只是借鉴他人并添加自己的理解,在这里就不对原理神马的进行分析啦,直接通过一个ViewGroup和View来进行探讨
因为只是简单测试,所以就没有写的那么的严谨
1.代码+注释
DecoreViewGroup
public class DecoreViewGroup extends ViewGroup{
public DecoreViewGroup(Context context) {
super(context);
}
public DecoreViewGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DecoreViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public DecoreViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int layoutWidth = r-l;
int left = 0;
int right = 0;
int top = 0;
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
// 换行:比较right,right如果大于Layout宽度,那么要换行
right = left + view.getMeasuredWidth();
if (right > layoutWidth) {
left = 0;
right = left + view.getMeasuredWidth();
top += view.getMeasuredHeight();
}
getChildAt(i).layout(left, top, right, top + view.getMeasuredHeight());
left += view.getWidth();
}
}
/**
* onInterceptTouchEvent 返回true 自己消费 子类收不到
* 返回false 子类消费
* @param event
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
Log.d("touchTest", "ViewGroup onInterceptTouchEvent ==="+ getDispatchSize(event.getAction()) + super.onInterceptTouchEvent(event));
return super.onInterceptTouchEvent(event);
}
/**
* dispatchTouchEvent 返回true 自己也消费不到
* 返回false 自己可以消费
* 返回super.dispatchTouchEvent(event) 默认为false
* 但是如果返回false , 就一定要调用super.dispatchTouchEvent(event)方法哦
* 否则的话onTouchEvent方法还是收不到
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d("touchTest", "ViewGroup dispatchTouchEvent ======"+ getDispatchSize(event.getAction()) + super.dispatchTouchEvent(event));
return super.dispatchTouchEvent(event);
}
public String getDispatchSize(int patch){
return patch == 0 ? "点击====" : patch == 1? "抬起====" : patch == 2 ? "移动====" : "其他====";
}
/**
* onTouchEvent 返回true 向上传递,将执行所有的事件
* 返回false 自己消费 不向上传递 执行DOWN事件之后不再执行(只有点击事件)
* 返回super.dispatchTouchEvent(event) 默认为false , 返回 super.onTouchEvent(event)才能收到点击事件
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("touchTest", "ViewGroup onTouchEvent ============"+ getDispatchSize(event.getAction())+ super.onTouchEvent(event));
return super.onTouchEvent(event);
}
}
DecoreView
public class DecoreView extends View {
public DecoreView(Context context) {
super(context);
}
public DecoreView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DecoreView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public DecoreView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public String getDispatchSize(int patch){
return patch == 0 ? "点击====" : patch == 1? "抬起====" : patch == 2 ? "移动====" : "其他====";
}
/**
* 控制自己
* dispatchTouchEvent 返回true 自己也消费不到
* 返回false 自己可以消费
* super.dispatchTouchEvent(event) 默认false 自己可以消费
* 但是如果返回false , 就一定要调用super.dispatchTouchEvent(event)方法哦
* 否则的话onTouchEvent方法还是收不到
* 如果返回true就不用啦,因为已经被强制拦截啦
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d("touchTest", "View dispatchTouchEvent ==========="+ getDispatchSize(event.getAction()) + super.dispatchTouchEvent(event));
return super.dispatchTouchEvent(event);
}
/**
* onTouchEvent 返回true 向上传递,将执行所有的事件 ,但是不会执行onclick()方法
* 返回false 自己消费 不向上传递 不会执行onclick
* 返回super.dispatchTouchEvent(event) 默认为false , 会执行onClick
* 子view的onTouchEvent没有返回True。则最后还是由ViewGroup去消费此事件
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("touchTest", "View onTouchEvent =================" + getDispatchSize(event.getAction()) + super.onTouchEvent(event));
return super.onTouchEvent(event);
}
}
代码大家直接copy到一个Demo中就行啦,有简单的一些注释,可以通过log来查看设置不同的返回值所执行的顺序
包装很简单,直接将DecoreView 套在DecoreViewGroup下就行啦,并设置不同的背景颜色来区分区域
在尝试了一上午之后,开始写写我对事件分发的理解
我的理解
(1).Activity中重写dispatchTouchEvent()
假如在activity中添加这么一段,那你activity下的所有控件都收不到点击事件啦
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return true;
}
dispatchTouchEvent主要是用来给自己分发的,感觉有点像ViewGroup中的onInterceptTouchEvent()
-
true:简单来说就是我想要这个事件,你不要继续分发下去了,就由我来消费这个事件,所以你点击控件相当于被activity 拦截了,你永远收不到事件
-
false:我不拦截啦,你继续往下走吧
-
super.dispatchTouchEvent():默认值就是false哟,相当于默认就不拦截,但是父类中的dispatchTouchEvent()方法还有 很多事情要处理,所以最好不要返回false,要不就返回true,要不就返回super.dispatchTouchEvent()
(2).ViewGroup中重写onInterceptTouchEvent()
onInterceptTouchEvent()方法主要是用来给子View分发的
-
true:子View的所有的事件分发回调都不会调用了,相当于隔绝了
-
false | super.dispatchTouchEvent():子View可以正常接收回调,不拦截继续向子View分发
(3).ViewGroup中重写dispatchTouchEvent()
-
true:将不会再调用onTouchEvent()方法啦,自己的事件回调已经被拦截掉了
-
super.dispatchTouchEvent() | false:继续向下分发
(4).ViewGroup中重写onTouchEvent()
-
super.dispatchTouchEvent() | true:如果在View设置了true,ViewGroup只能收到一次onTouchEvent的ACTION_UP事件,但是可以收到 dispatchTouchEvent->onInterceptTouchEvent事件,相当于会向上传递给ViewGroup的事件
-
false:只有返回false ,ViewGroup中onTouchEvent才能收到事件,由dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent
(5).View中重写dispatchTouchEvent()
-
true:将不会再调用onTouchEvent()方法啦,自己的事件回调已经被拦截掉了
-
super.dispatchTouchEvent() | false:继续向下分发,下一步是onTouchEvent
(6).View中重写onTouchEvent()
-
super.dispatchTouchEvent() | true:如果在View设置了true,ViewGroup只能收到一次onTouchEvent的ACTION_UP事件,但是可以收到 dispatchTouchEvent->onInterceptTouchEvent事件,相当于会向上传递给ViewGroup的事件
-
false:只有返回false ,ViewGroup中onTouchEvent才能收到事件,由dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent