1、简述
事件分发 ,是手机对手指触摸事件处理的过程;也就是寻找触摸事件处理者,并进行处理的过程
对于android开发者来说,事件分发没有使用过,也是了解过,其实android的事件分发是分为多个模块操作:硬件检测—framework层事件拦截—window内部处理—ViewGroup处理— View处理;我们常常需要处理过程也就是最后两个,笔者觉得先从view出发,开始撸起
2、基础知识
触摸事件是一系列事件,主要处理的事件ACTION_DOWN、ACTION_MOVE、ACTION_CANCEL、ACTION_UP、ACTION_POINTER_DOWN、ACTION_POINTER_UP,事件是以ACTION_DOWN开始,以ACTION_UP或者ACTION_CANCEL事件结束
ACTION_DOWN ----> 其它事件 -----> ACTION_UP/ACTION_CANCEL
2.1 事件类型
ACTION_DOWN : 第一个手指按下事件
ACTION_UP: 最后一个手指抬起事件
ACTION_CANCEL:取消事件,也即是不能得到后续事件
ACTION_MOVE : 移动事件
ACTION_POINTER_DOWN:多个手指时按下事件
ACTION_POINTER_UP:多个手指时,抬起事件
对于触摸事件,响应动作,有回调和监听
2.2 常用处理动作
View.OnTouchListener: 触摸监听
View.OnClickListener: 单击事件监听
View.OnLongClickListener: 长按事件监听
View.onTouchEvent:事件处理回调
2.3 view事件处理方法
dispatchTouchEvent(MotionEvent ev): 事件处理开始方法
onInterceptTouchEvent(MotionEvent ev):在 dispatchTouchEvent方法内调度,拦截事件,ViewGroup内方法
onTouchEvent(MotionEvent event):回调事件处理方法
3、具体处理流程
对于处理流程,我们从View源码中dispatchTouchEvent方法分析,只分析了我认为有用的信息
3.1 dispatchTouchEvent
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//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;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
可以看出
- 有个安全策略,如果满足,则view可以继续处理,否则我们不用管(mInputEventConsistencyVerifier是个不可变量);
- view处理事件首先查看有没有OnTouchListener对象,存在的话进行处理,处理结果为true,则流程结束
- OnTouchListener对象为空或者处理结果为false,则调用回调方法onTouchEvent
重点:OnTouchListener监听一定会执行,如若处理结果为false,则onTouchEvent才会执行
3.2 onTouchEvent
有没有问咋没有onInterceptTouchEvent方法,view没有这个方法
- TouchDelegate对象处理
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
可以通过下面方法设置
public void setTouchDelegate(TouchDelegate delegate) {
mTouchDelegate = delegate;
}
- ACTION_DOWN 事件处理
case MotionEvent.ACTION_DOWN:
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
}
mHasPerformedLongPress = false;
if (!clickable) {
checkForLongClick( ViewConfiguration.getLongPressTimeout(), x,y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
break;
}
if (performButtonActionOnTouchDown(event)) {
break;
}
boolean isInScrollingContainer = isInScrollingContainer();
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
setPressed(true, x, y);
checkForLongClick( ViewConfiguration.getLongPressTimeout(),x, y,TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
break;
- checkForLongClick:检测长按事件,通过handler的postDelay延迟执行和remove来取消执行, 执行的长按任务为CheckForLongPress对象,其中执行了OnLongClickListener监听
public void run() {
if ((mOriginalPressedState == isPressed()) && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
recordGestureClassification(mClassification);
if (performLongClick(mX, mY)) { // performLongClick 方法中执行了 OnLongClickListener
mHasPerformedLongPress = true;
}
}
}
- ACTION_UP关键代码分析
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
- 通过PerformClick对象或者执行performClickInternal方法来回调OnClickListener监听
- ACTION_CANCEL事件
case MotionEvent.ACTION_CANCEL:
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break;
取消长按事件执行,重置标志
3、流程总结
- 执行优先级
OnTouchListener.onTouch > onTouchEvent > OnClickListener.onClick or OnLongClickListener.onLongClick
- 如果事件传到view,那么OnTouchListener.onTouch一定执行;如果执行结果为false,才执行onTouchEvent
- OnClickListener.onClick or OnLongClickListener.onLongClick是在onTouchEvent方法中执行,且是有且只会执行其中一种
请继续查看 android事件分发(2):ViewGroup