一.前言
刚学习Android的朋友们可能以会使用控件为主要任务,但是我们随着学习Android的深入,会不断接触到Android的事件流,也就是Android的事件分布机制.可能有些人觉得这和你有什么关系,你可能觉得不了解我照样做app,但是博主建议你还是熟悉它比较好,因为博客学习Android的时候,需要熟悉它的时候有以下几种:
1.自定义控件的时候
2.在某些情况下的bug,比如ListView的OnItemClickListener的onItemClick方法失效
3.某些情况下要获取用户的按键情况,比如你想屏蔽后退键
还有其他的一些情况,博客暂时想到的就这些了,不过肯定还有其他你需要熟悉它的时候
今天博客就重点基于Android 6.0的源码,解析一下事件的分发机制
首先先引用别人的一张我们看到的视图层次机构图,因为博主自己画就太丑了,哈哈
从这里我们可以看出,ViewGroup是一个容器,它可以承载View或者其他的ViewGroup容器,而ViewGroup本身又继承了View,所以ViewGroup是View的一个扩展.这是对上图的一个简单的介绍,好了,下面是正文
二.解析View的事件分发
首先贴出View的事件分发的主要源代码
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
final int actionMasked = event.getActionMasked();
//安全性的拦截方法,检查view是不是被覆盖了
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
//在事件分发的时候做一个安全性的检测,检测View是不是被遮盖,如果被遮盖返回false,否则返回true,true表示应该分发事件
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
// Window is obscured, drop this touch.
return false;
}
return true;
}
部分无关紧要的代码已经剔除,不然代码不仅长,而且牵扯太多其他的东西
从上面的代码我们看到了View的事件分发,在分发的时候,我们可以清楚的看到调用了一
个检测安全性的方法,如果通过了才可以分发事件.检测啥,上面我已经清除的注释了,这里
不再陈述.
紧接着程序拿到一个ListenerInfo对象,这个对象是什么呢?从字面上理解,应该是一个有关监听的信息的类,那我们就去查看一下
static class ListenerInfo {
protected OnFocusChangeListener mOnFocusChangeListener;
private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
protected OnScrollChangeListener mOnScrollChangeListener;
private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
public OnClickListener mOnClickListener;
protected OnLongClickListener mOnLongClickListener;/
protected OnContextClickListener mOnContextClickListener;
protected OnCreateContextMenuListener mOnCreateContextMenuListener;
private OnKeyListener mOnKeyListener;
private OnTouchListener mOnTouchListener;
private OnHoverListener mOnHoverListener;
private OnGenericMotionListener mOnGenericMotionListener;
private OnDragListener mOnDragListener;
private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
}
一看,原来是一个接口保存的'仓库'啊,我们可以看到View有关的监听是不是都在里头了呢,点击事件,长按事件。。。。,很多很多,应该是为了避免在View中写太多的成员变量,所以才分离出来的
那我们上面的代码就可以看懂啦!过了安全的检测之后就是四个判断
1.判断保存接口信息的li是不是为空
2.li里面的mOnTouchListener接口是不是为空,说白了也就是你有没有设置这个View的触摸事件
3.View是不是不能用的状态,默认是可用的,通过xx.setEnabled(false)就可以设置成无用的状态啦
4.触摸接口mOnTouchListener的方法onTouch返回的是不是true
这里也就很清楚的看到了,View通过设置监听回调的mOnTouchListener的触摸事件onTouch是比View的其他事件都要早的,如果你监听了该事件,那么你就有绝对的权利控制事件是否能流到其他的地方
如果在设置的触摸接口的回调方法onTouch方法中返回了一个true,那么变量result就会被置为true,所以下面的View自身的onTouch事件将不会再被调用,反之View自身的onTouchEvent事件被调用,而View的点击事件就是在onTouchEvent方法中调用的,所以View的点击事件如果失效了,就针对View本身来说有如下可能:
1.事件被mOnTouchListener的方法onTouch消费了,也就是这里返回的是true
2.你重写View的onTouchEvent方法的时候没有调用super.onTouchEvent(e)方法
下一篇介绍ViewGroup的事件分发
阅读完如果觉得哪里有问题或者需要纠正的地方请留言,不甚感激~~~