转眼毕业已经一年多时间了,从毕业开始看事件分发,那个时候看了好些天,看的迷迷糊糊的,半年前又重新翻看了一下,算是理解了一些了,今天重新理了一下相关知识,算是一个复习总结吧
对于事件分发,首先的是方法返回true表示消费了事件,false表示继续传递
- 首先点击事件产生之后,是交给activity来处理的,而activity主要管理的是界面的生命周期,而对于界面的显示等都是由activity中的成员window来管理的,算是一种职责上的分离吧。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
- window中维护的最上层的view就decorView,我们activity中添加的界面就是添加到这个view中的,所以事件会传递到decorView中
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
//DecorView
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
- DecorView实际上也是继承至viewgroup的,所以这里调用的其实viewgroup的dispatchTouchEvent(MotionEvent ev)方法,大致流程如下
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//down事件的时候初始化
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//是否拦截的标记
final boolean intercepted;
//如果是down事件或者target不为空则会进入这个方法判断(有子view可以消费这个事件)
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
//如果子事件设置了不让父控件拦截则不会进行onInterceptTouchEvent判断了
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//若需要拦截这个事件,则重写这个方法,返回true
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
//如果事件被自己拦截了,就不会执行分发给子view的事件了
if (!canceled && !intercepted) {
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
//如果有子view则倒序遍历看子view是否能接收到这个事件,相对于点击的位置是否在子view上
for (int i = childrenCount - 1; i >= 0; i--) {
//传递给子view是否可以处理
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//若可以拦截这个事件则给target添加view
break;
}
}
}
}
}
if (mFirstTouchTarget == null) {
//因为target中没有元素,则调用这个方法,传入的子view是空,去回调自己父类view的dispatchTouchEvent方法
handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
} else{
//当传给子view的事件又被他自己拦截了,就会传递给子view一个取消事件,然后清空target
dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)
}
return handled;
}
- 当上述流程中这个viewgroup拦截了事件的话,target就会一直为空,就会执行下放为空的方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
if (child == null) {
//如果child为空就调用父类的dispatchTouchEvent方法
handled = super.dispatchTouchEvent(event);
} else {
//如果child不为空就传递给child的dispatchTouchEvent去继续分发
handled = child.dispatchTouchEvent(event);
}
return handled;
}
- 然后在view的dispatchTouchEvent中
public boolean dispatchTouchEvent(MotionEvent event) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果上方得到了result=true,则onTouchEvent就会被屏蔽,也不会有click触发了
if (!result && onTouchEvent(event)) {
result = true;
}
return result;
}
- 然后在最后的onTouchEvent中的up事件中会调用熟悉的OnClickListener
public boolean onTouchEvent(MotionEvent event) {
//如果view是可以点击的,这个方法就会一直返回true的
switch (action) {
case MotionEvent.ACTION_UP:
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
return result;
}