源码解析Android中的事件处理
Posted Iaouei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码解析Android中的事件处理相关的知识,希望对你有一定的参考价值。
一,事件处理
android提供了两套事件处理机制:
1.基于监听的事件处理。
2.基于回调的事件处理。
基于回调的事件处理用于处理一些具有通用性的事件,基于监听的事件处理用于处理与具体业务相关的事件。
基于监听的事件处理
基于监听的事件处理是在指定view组件上绑定指定的监听器。比如点击事件:
可以以匿名内部类形式绑定监听器:
button.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
);
或者:
// Create an anonymous implementation of OnClickListener
private OnClickListener mCorkyListener = new OnClickListener()
public void onClick(View v)
// do something when the button is clicked
;
protected void onCreate(Bundle savedValues)
...
// Capture our button from layout
Button button = (Button)findViewById(R.id.corky);
// Register the onClick listener with the implementation above
button.setOnClickListener(mCorkyListener);
...
还可以将 OnClickListener 作为 Activity 的一部分来实现更为方便。这样可以避免加载额外的类和分配对象:
public class ExampleActivity extends Activity implements OnClickListener
protected void onCreate(Bundle savedValues)
...
Button button = (Button)findViewById(R.id.corky);
button.setOnClickListener(this);
// Implement the OnClickListener callback
public void onClick(View v)
// do something when the button is clicked
...
view类声明监听器和执行点击事件中:
//监听器
public interface OnClickListener
void onClick(View v);
//注册监听器 方法
public void setOnClickListener(@Nullable OnClickListener l)
if (!isClickable())
setClickable(true);
getListenerInfo().mOnClickListener = l;
//声明监听器
static class ListenerInfo
public OnClickListener mOnClickListener;
//执行监听动作
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;
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
基于回调的事件处理
基于回调的事件处理就是自定义UI组件或者Activity重写事件方法,比如View类和Activity的父类都实现了KeyEvent.Callback接口中的一系列回调函数,因此在自定义View或Activity中可以通过重写相关方法来实现特定功能:
KeyEvent.Callback接口:
public interface Callback
//该返回值用于标识该处理函数是否能完全处理该事件
// 返回true,表明该函数已完全处理该事件,该事件不会传播出去
// 返回false,表明该函数未完全处理该事件,该事件会传播出去
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
重写相关方法:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
return super.onKeyDown(keyCode, event);
二,相关类概述
关于事件,android中有一个InputEvent类,它是针对所有事件的共有特点提取出来的一个统一的抽象类,此类结构简单,可以参看InputEvent .java 。其有两个子类:MotionEvent(位移事件)和KeyEvent(按键事件)。这两个类都定义了大量的常量,弄明白这些常量对读懂这两个类至关重要。还有InputDevice类,这个类包含关于输入设备定义的常量和其他一些常量。前面两个类中的常量几乎都是此类的输入设备的相关配置。
InputDevice类
输入设备:
public static final int SOURCE_CLASS_MASK = 0x000000ff;
public static final int SOURCE_CLASS_NONE = 0x00000000;
public static final int SOURCE_CLASS_BUTTON = 0x00000001;
public static final int SOURCE_CLASS_POINTER = 0x00000002;
public static final int SOURCE_CLASS_TRACKBALL = 0x00000004;
public static final int SOURCE_CLASS_POSITION = 0x00000008;
public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
public static final int SOURCE_UNKNOWN = 0x00000000;
public static final int SOURCE_KEYBOARD = 0x00000100 | SOURCE_CLASS_BUTTON;
public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON;
public static final int SOURCE_TOUCHSCREEN = 0x00001000 | SOURCE_CLASS_POINTER;
public static final int SOURCE_MOUSE = 0x00002000 | SOURCE_CLASS_POINTER;
public static final int SOURCE_STYLUS = 0x00004000 | SOURCE_CLASS_POINTER;
public static final int SOURCE_BLUETOOTH_STYLUS =
0x00008000 | SOURCE_STYLUS;
public static final int SOURCE_TRACKBALL = 0x00010000 | SOURCE_CLASS_TRACKBALL;
public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
public static final int SOURCE_ROTARY_ENCODER = 0x00400000 | SOURCE_CLASS_NONE;
public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK;
public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
public static final int SOURCE_ANY = 0xffffff00;
MotionEvent类
当用户触摸屏幕时将创建一个MotionEvent对象。MotionEvent包含关于发生触摸的位置和时间等细节信息。MotionEvent对象被传递到程序中合适的方法比如View对象的onTouchEvent()方法中。在这些方法中我们可以分析MotionEvent对象那个,以决定要执行的操作。
MotionEvent对象是与用户触摸相关的时间序列,该序列从用户首次触摸屏幕开始,经历手指在屏幕表面的任何移动,直到手指离开屏幕时结束。手指的初次触摸(ACTION_DOWN操作),滑动(ACTION_MOVE操作)和松开(ACTION_UP)都会创建MotionEvent对象。移动过程中会产生大量事件,每个事件都会产生对应的MotionEvent对象记录发生的操作,触摸的位置,使用的多大压力,触摸的面积,合适发生,以及最初的ACTION_DOWN和时发生等相关的信息。
action:触摸:
/**
* 按下
*/
public static final int ACTION_DOWN = 0;
/**
* 松开
*/
public static final int ACTION_UP = 1;
/**
* 移动
*/
public static final int ACTION_MOVE = 2;
/**
* 触摸动作取消
*/
public static final int ACTION_CANCEL = 3;
/**
* 触摸动作超出边界
*/
public static final int ACTION_OUTSIDE = 4;
/**
* 多点触摸按下动作
*/
public static final int ACTION_POINTER_DOWN = 5;
/**
* 多点离开动作
*/
public static final int ACTION_POINTER_UP = 6;
action:鼠标:
/**
* 鼠标在view上移动
*/
public static final int ACTION_HOVER_MOVE = 7;
/**
* 滚动
*/
public static final int ACTION_SCROLL = 8;
/**
* 鼠标进入view
*/
public static final int ACTION_HOVER_ENTER = 9;
/**
* 鼠标离开view
*/
public static final int ACTION_HOVER_EXIT = 10;
/**
* 鼠标按住按键
*/
public static final int ACTION_BUTTON_PRESS = 11;
/**
* 鼠标释放按键
*/
public static final int ACTION_BUTTON_RELEASE = 12;
action:特殊:这几个比较难以理解,建议阅读
android触控,先了解MotionEvent(一)
/**
* 动作位掩码
*/
public static final int ACTION_MASK = 0xff;
/**
* 指针位掩码
*/
public static final int ACTION_POINTER_INDEX_MASK = 0xff00;
/**
*
*/
public static final int ACTION_POINTER_INDEX_SHIFT = 8;
边缘:
/**
* 上边
*/
public static final int EDGE_TOP = 0x00000001;
/**
* 下边
*/
public static final int EDGE_BOTTOM = 0x00000002;
/**
* 左边
*/
public static final int EDGE_LEFT = 0x00000004;
/**
* 右边
*/
public static final int EDGE_RIGHT = 0x00000008;
触摸屏幕相关的常量:AXIS_打头
BUTTON鼠标按键:
/**
* 左键
*/
public static final int BUTTON_PRIMARY = 1 << 0;
/**
* 右键
*/
public static final int BUTTON_SECONDARY = 1 << 1;
/**
* 中间键
*/
public static final int BUTTON_TERTIARY = 1 << 2;
/**
* 后退键
*/
public static final int BUTTON_BACK = 1 << 3;
/**
* 前进键
*/
public static final int BUTTON_FORWARD = 1 << 4;
/**
*
*/
public static final int BUTTON_STYLUS_PRIMARY = 1 << 5;
/**
*
*/
public static final int BUTTON_STYLUS_SECONDARY = 1 << 6;
TOOL触摸的工具:
/**
* 未知
*/
public static final int TOOL_TYPE_UNKNOWN = 0;
/**
* 手指
*/
public static final int TOOL_TYPE_FINGER = 1;
/**
* 手写笔
*/
public static final int TOOL_TYPE_STYLUS = 2;
/**
* 鼠标
*/
public static final int TOOL_TYPE_MOUSE = 3;
/**
* 橡皮擦
*/
public static final int TOOL_TYPE_ERASER = 4;
类中的方法大多方法是对触摸对象属性的获取,大都是对jni层的封装。
并且类中给出触摸事件的判断方法:
public final boolean isTouchEvent()
return nativeIsTouchEvent(mNativePtr);
值得注意的是此类只是对触摸事件的定义,没有触摸事件的分发方法和相关监听器接口。
KeyEvent类
类中定义的key常量都是以KEYCODE_开头,定义了android系统所有的各种按键。包括手机键盘按键,类似电脑键盘的功能键(KEYCODE_F+数字),小键盘键(KEYCODE_NUMPAD_),字母键,符号键等几乎所有的按键,多媒体键(KEYCODE_MEDIA_),游戏手柄按键(KEYCODE_BUTTON_),TV按键(KEYCODE_TV_),还有组合键(比如KEYCODE_ALT_LEFT)等。在方法中并提供了一些方法判断是否是某个或某组按键,比如:boolean isGamepadButton(int keyCode),boolean isShiftPressed()等。
然后定义了按键动作的常量:
//按下
public static final int ACTION_DOWN = 0;
//松开
public static final int ACTION_UP = 1;
//按多个键
public static final int ACTION_MULTIPLE = 2;
和大量META_开头与FLAG_开头的常量。
KeyEvent类中主要的逻辑方法是KeyEvent的事件分发,其他方法主要是获取按键的属性和判断是否是某个键或某个组里的键。
三,事件处理
事件传递过程:
推荐阅读Android事件处理流程分析和深入理解android内核设计思想12.2 事件投递流程。
由上图可知,事件经一系列处理,最终传到DecorView。DecorView是PhoneWindow类的内部类并且该类继承FrameLayout。PhoneWindow类位于/frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java。该类继承于Window类,是Window类的具体实现。
KeyEvent事件处理
PhoneWindow#DecorView#dispatchKeyEvent(KeyEvent event):
@Override
public boolean dispatchKeyEvent(KeyEvent event)
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
//此段正常手机几乎用不到
if (isDown && (event.getRepeatCount() == 0))
//首先处理面板上的键,如果按住一个键不松,尝试执行包含它的快捷键
if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode))
boolean handled = dispatchKeyShortcutEvent(event);//分发快捷键事件
if (handled)
return true;
// 如果面板打来,执行一个快捷键,不用按面板上的按键
if ((mPreparedPanel != null) && mPreparedPanel.isOpen)
if (performPanelShortcut(mPreparedPanel, keyCode, event, 0))
return true;
//如果按键事件发生时窗口没销毁
if (!isDestroyed())
final Callback cb = getCallback();
//mFeatureId表示窗口的ID,应用的DecorView为-1
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)//交给实现Callback的类处理,比如大多情况下都是使用的是Activity。
: super.dispatchKeyEvent(event);//交给父类ViewGroup处理//执行这步的这种情况比较少
if (handled)
return true;
//此语句很少情况用到,比如开机键用
//如果按键事件发生时窗口已经销毁
//如果是KeyEvent.ACTION_DOWN,执行PhoneWindow.this.onKeyDown
//如果不是,执行PhoneWindow.this.onKeyUp
//返回isDown
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
Activity#dispatchKeyEvent(KeyEvent event)
public boolean dispatchKeyEvent(KeyEvent event)
onUserInteraction();
// 如果是菜单键事件, action bars打开菜单
if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event))
return true;
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) //事件交给Window处理
return true;
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
//如果view层次结构没处理,即win.superDispatchKeyEvent(event)返回是flase
//则交给KeyEvent本身的dispatch方法,
//Activity的各种回调方法会被触发则交给KeyEvent本身的dispatch方法,Activity的各种回调方法会被触发
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
Window是一个抽象类,其中的方法都是抽象方法:
public abstract boolean superDispatchTouchEvent(MotionEvent event);
唯一实现类是 PhoneWindow:
@Override
public boolean superDispatchKeyEvent(KeyEvent event)
return mDecor.superDispatchKeyEvent(event);
PhoneWindow#DecorView#superDispatchKeyEvent(KeyEvent event):
public boolean superDispatchKeyEvent(KeyEvent event)
// 如果按键是后退键.
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
final int action = event.getAction();//
// Back cancels action modes first.
if (mPrimaryActionMode != null) //如果动作模式不为空
if (action == KeyEvent.ACTION_UP)
mPrimaryActionMode.finish();//结束动作
return true;
//如果不是返回键,则交给父类ViewGroup处理,并返回处理结果
return super.dispatchKeyEvent(event);
ViewGroup#dispatchKeyEvent(KeyEvent event):
@Override
public boolean dispatchKeyEvent(KeyEvent event)
//keyevent一致性检测用的,可忽略
if (mInputEventConsistencyVerifier != null)
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
//如果此ViewGroup是focused或者具体的大小被设置了,交给父类View处理
if (super.dispatchKeyEvent(event))
return true;
else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) //mFocused为处于焦点的View
//否则交给处于焦点的View处理
if (mFocused.dispatchKeyEvent(event))
return true;
if (mInputEventConsistencyVerifier != null)
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
//如果都没处理返回false
return false;
View的dispatchKeyEvent(KeyEvent event):
public boolean dispatchKeyEvent(KeyEvent event)
if (mInputEventConsistencyVerifier != null)
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
// 给出所有的关联的key 监听器, a first crack at the event.
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//调用onKeyListener,如果它非空且view是ENABLED状态,监听器优先触发
if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event))
return true;
//如果上面if语句没执行,则调用KeyEvent.dispatch方法,并将view对象本身作为参数传递进去,
//view的各种callback方法在这里被触发
if (event.dispatch(this, mAttachInfo != null
? mAttachInfo.mKeyDispatchState : null, this))
return true;
if (mInputEventConsistencyVerifier != null)
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
//还没处理掉返回false
return false;
KeyEvent#dispatch(Callback receiver, DispatcherState state, Object target):
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target)
switch (mAction)
case ACTION_DOWN:
//a&=~b的意思就是 a= a & (~b),& ~都是位操作运算符,&是与运算,~是取反运算。
mFlags &= ~FLAG_START_TRACKING;//先清掉START_TRACKING标记
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);//receiver是Callback实例,调用Callback接口的onKeyDown方法,View和Activity都是此接口的实现者
if (state != null) // 一般都成立
//如果 receiver.onKeyDown返回true了且不是repeated,并且也没有开始tracking
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0)
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);//则开始tracking当前的KeyEvent和target
else if (isLongPress() && state.isTracking(this)) // 处理长按
try
if (receiver.onKeyLongPress(mKeyCode, this))
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);//记录此event已经有long press发生了
res = true;//设置为事件已经处理
catch (AbstractMethodError e)
return res;//返回down事件处理的结果
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
if (state != null)
state.handleUpEvent(this);
return receiver.onKeyUp(mKeyCode, this);// 最后调用receiver.onKeyUp方法,并返回结果
case ACTION_MULTIPLE:
final int count = mRepeatCount;
final int code = mKeyCode;
if (receiver.onKeyMultiple(code, count, this))
return true;
if (code != KeyEvent.KEYCODE_UNKNOWN)
mAction = ACTION_DOWN;
mRepeatCount = 0;
boolean handled = receiver.onKeyDown(code, this);
if (handled)
mAction = ACTION_UP;
receiver.onKeyUp(code, this);
mAction = ACTION_MULTIPLE;
mRepeatCount = count;
return handled;
return false;
return false;
KeyEvent.Callback
public interface Callback
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
Activity和View都实现了KeyEvent.Callback。
方法调用流程图大致可以如下:
TouchEvent事件分发
PhoneWindow#DecorView#dispatchTouchEvent(MotionEvent event):
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
final Callback cb = getCallback();
//mFeatureId表示窗口的ID,应用的DecorView为-1
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)//调用Window.Callback实现类的dispatchTouchEvent(ev)方法,常用的为Activity,另外还用对话框等类
: super.dispatchTouchEvent(ev);//调用DecorView父类dispatchTouchEvent(ev)方法
Activity#dispatchTouchEvent(MotionEvent ev):
public boolean dispatchTouchEvent(MotionEvent ev)
if (ev.getAction() == MotionEvent.ACTION_DOWN)
onUserInteraction();
//交给Window去处理
if (getWindow().superDispatchTouchEvent(ev))
return true;
// Window处理失败,退回来自己在onTouchEvent中处理
return onTouchEvent(ev);
Window是一个抽象类,其中的方法都是抽象方法:
public abstract boolean superDispatchTouchEvent(MotionEvent event);
唯一实现类是 PhoneWindow:
@Override
public boolean superDispatchTouchEvent(MotionEvent event)
//PhoneWindow 把事件分发的任务交给DecorView处理
return mDecor.superDispatchTouchEvent(event);
PhoneWindow#DecorView#superDispatchTouchEvent(MotionEvent event):
public boolean superDispatchTouchEvent(MotionEvent event)
//DecorView又把事件分发的任务交给父类即ViewGroup处理
return super.dispatchTouchEvent(event);
ViewGroup#dispatchTouchEvent(MotionEvent ev):
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
if (mInputEventConsistencyVerifier != null)
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost())
ev.setTargetAccessibilityFocus(false);
boolean handled = false;//event是否被处理
if (onFilterTouchEventForSecurity(ev))
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
//判断是否是ACTION_DOWN事件
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) //ACTION_DOWN是后续事件的起点
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
/*
* 在这个函数中最终将mFirstTouchTarget设为null。
* mFirstTouchTarget代表的就是一个事件序列中第一个拦截的对象,
* 所以这里需要重置。
*/
cancelAndClearTouchTargets(ev);//清除以前的所有状态
/*
* 如果事件是ACTION_DOWN,
* ViewGroup就会在resetTouchState中重置下面的FLAG_DISALLOW_INTERCEPT标志位。
* 重置的方式是这样的:mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
*/
resetTouchState();//重置触摸状态
// 检查interception(拦截).
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null)
/*
* 这个if中需要满足两个条件:
* (1)actionMasked == MotionEvent.ACTION_DOWN:
* 该事件是否为点击下按事件时成立,就是说新的一轮事件到来
* (2)mFirstTouchTarget != null:
* 当ViewGroup不拦截事件并将事件交给子元素处理时,成立,mFirstTouchTarget指向这个子元素。
* 而且在ViewGroup中,默认onInterceptTouchEvent返回false,它是不拦截任何事件的,
* 但是在LinearLayout中可能就会拦截啊,可以改写啊。
* 而且,当第二个条件成立时,此时发生的事件序列就是ACTION_MOVE或者ACTION_UP,都会进入到这个if语句中。
* */
/*
* 所以说呢,当子元素成功拦截了事件或者下按事件发生的时候就会进入if语句。
* 所以说呢,如果子元素没有处理,并且是move和up发生的时候就无法进入该if语句。
* 但为什么这样设定呢,因为如果子元素没有处理的话,事件序列中的其他事件就会直接由ViewGroup来处理了,
* 不需要来这里来判断一下到底要不要拦截事件了。那如果是move和up也是同样的,不需要来这里来判断要不要拦截事件。
* */
/*
* 也就相当于说,一个事件,第一次因为ACTION_DOWN进入这里,然后ViewGroup判断是否来拦截。
* 之后在子元素成功处理后,因为子元素是可以通过FLAG_DISALLOW_INTERCEPT标志位来干预父元素的事件分发过程,所以又来这里来要看是否拦截。
* */
/*
* 为什么总说一旦父元素拦截ACTION_DOWN以后其他的事件序列就只能由父元素来处理呢?
* 是因为如果父元素拦截了ACTION_DOWN,那么mFirstTouchTarget == null
* 当ACTION_MOVE和ACTION_UP到来的时候,这条if语句就不会进入了,
* 然后intercepted = true;表示事件序列由父元素全拦截了。
* */
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null)
/*
* 通常事件传递过程是由外向内的,
* 但是通过 requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,
* 不过ACTION_DOWN事件除外。
* 干预表现在子元素已经拦截了事件,
* 但是可以通过requestDisallowInterceptTouchEvent来控制
* ACTION_MOVE和ACTION_UP能不能够进入到这里来。
* */
/*
* FLAG_DISALLOW_INTERCEPT一旦设置后,ViewGroup将无法拦截处理ACTION_DOWN以外的其他点击事件了。
* 因为在事件分发时,ACTION_DOWN会重置FLAG_DISALLOW_INTERCEPT标志位,表示另一次事件开始。
* */
/*
* 子View干涉ViewGroup的过程:
* 初始化:mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
* 在子View中FLAG_DISALLOW_INTERCEPT被重置,也就是要去干扰,
* 然后mGroupFlags & FLAG_DISALLOW_INTERCEPT为1
* 然后(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0 为true
* 然后disallowIntercept为true
* 然后导致if (!disallowIntercept)无法进入。
* */
/*
* FLAG_DISALLOW_INTERCEPT标志位有什么用呢?
* 当面对滑动冲突时,我们可以考虑用这种方法去解决问题。
* */
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) //disallowIntercept为true 如果允许拦截
/*
* 所以说onInterceptTouchEvent并不是每次事件都会被调用的。
* 而dispatchTouchEvent却会在每次都调用。
* 对于原始的ViewGroup,onInterceptTouchEvent会返回false,
* 但是对于你自己写的LinearLayout,则可以修改这个函数,
* 让它对ACTION_DOWN、ACTION_MOVE、ACTION_UP做出不同的选择。
* */
intercepted = onInterceptTouchEvent(ev);//判断是否要真正执行拦截
ev.setAction(action); // restore action in case it was changed
else
intercepted = false;//不需要拦截
else //如果不是down事件并且mFirstTouchTarget为空
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;//继续拦截
// 如果已经拦截intercepted,开始正常的事件分发.
// 或者已经有view正在处理这个手势,也正常分发.
if (intercepted || mFirstTouchTarget != null)
ev.setTargetAccessibilityFocus(false);
// 检查 cancelation(叫停).
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
/*
* 当ViewGroup不拦截事件的时候,intercepted=false,事件会向下分发由它的子View进行处理
* 所以说一旦ViewGroup拦截了事件,intercepted=true,
* 意味着事件序列中的任何事件都不再会传给子元素了,由父元素全权处理。
* 所以intercepted=true一定要谨慎设置。
*/
if (!canceled && !intercepted) //不拦截的情况,它的子对象处理它
// If the event is targeting accessiiblity focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE)
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;//子对象个数
if (newTouchTarget == null && childrenCount != 0) //子对象个数不为0
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive以上是关于源码解析Android中的事件处理的主要内容,如果未能解决你的问题,请参考以下文章