什么是事件
點(diǎn)擊事件(Touch事件)

怎么描述事件: MotionEvent
事件分發(fā)的本質(zhì): 將點(diǎn)擊事件(MotionEvent)傳遞到某個(gè)具體的View & 處理的整個(gè)過(guò)程
事件分發(fā)的過(guò)程
事件的傳遞 activity ->viewgroup ->view

事件分發(fā)方法
dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

下面我們針對(duì)源碼 對(duì)這幾個(gè)方法進(jìn)行分析
Activity的事件分發(fā)機(jī)制

getWindow().superDispatchTouchEvent(ev)
window只有一個(gè)實(shí)現(xiàn)類PhoneWindow,所以我們看PhoneWindow里邊是怎么實(shí)現(xiàn)的

mDecor是DecorView 我們點(diǎn)進(jìn)去看DecorView會(huì)發(fā)現(xiàn)它繼承FrameLayout

即將事件傳遞到ViewGroup去處理。
我們總結(jié)一下就是
/**
* 源碼分析:Activity.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 僅貼出核心代碼
// ->>分析1
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
// 若getWindow().superDispatchTouchEvent(ev)的返回true
// 則Activity.dispatchTouchEvent()就返回true,則方法結(jié)束。即 :該點(diǎn)擊事件停止往下傳遞 & 事件傳遞過(guò)程結(jié)束
// 否則:繼續(xù)往下調(diào)用Activity.onTouchEvent
}
// ->>分析3
return onTouchEvent(ev);
}
/**
* 分析1:getWindow().superDispatchTouchEvent(ev)
* 說(shuō)明:
* a. getWindow() = 獲取Window類的對(duì)象
* b. Window類是抽象類,其唯一實(shí)現(xiàn)類 = PhoneWindow類
* c. Window類的superDispatchTouchEvent() = 1個(gè)抽象方法,由子類PhoneWindow類實(shí)現(xiàn)
*/
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
// mDecor = 頂層View(DecorView)的實(shí)例對(duì)象
// ->> 分析2
}
/**
* 分析2:mDecor.superDispatchTouchEvent(event)
* 定義:屬于頂層View(DecorView)
* 說(shuō)明:
* a. DecorView類是PhoneWindow類的一個(gè)內(nèi)部類
* b. DecorView繼承自FrameLayout,是所有界面的父類
* c. FrameLayout是ViewGroup的子類,故DecorView的間接父類 = ViewGroup
*/
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
// 調(diào)用父類的方法 = ViewGroup的dispatchTouchEvent()
// 即將事件傳遞到ViewGroup去處理,詳細(xì)請(qǐng)看后續(xù)章節(jié)分析的ViewGroup的事件分發(fā)機(jī)制
}
// 回到最初的分析2入口處
/**
* 分析3:Activity.onTouchEvent()
* 調(diào)用場(chǎng)景:當(dāng)一個(gè)點(diǎn)擊事件未被Activity下任何一個(gè)View接收/處理時(shí),就會(huì)調(diào)用該方法
*/
public boolean onTouchEvent(MotionEvent event) {
// ->> 分析5
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
// 即 只有在點(diǎn)擊事件在Window邊界外才會(huì)返回true,一般情況都返回false,分析完畢
}
/**
* 分析4:mWindow.shouldCloseOnTouch(this, event)
* 作用:主要是對(duì)于處理邊界外點(diǎn)擊事件的判斷:是否是DOWN事件,event的坐標(biāo)是否在邊界內(nèi)等
*/
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
// 返回true:說(shuō)明事件在邊界外,即 消費(fèi)事件
return true;
}
// 返回false:在邊界內(nèi),即未消費(fèi)(默認(rèn))
return false;
}
ViewGroup的事件分發(fā)
剛剛分析了activity的事件分發(fā),最后會(huì)分發(fā)到ViewGroup 實(shí)現(xiàn)了activity-->ViewGroup
public boolean dispatchTouchEvent(MotionEvent ev) {
// 分析1:ViewGroup每次事件分發(fā)時(shí),都需調(diào)用onInterceptTouchEvent()詢問(wèn)是否攔截事件
//我們可以通過(guò)改變disallowIntercept的值來(lái)讓ViewGroup不對(duì)事件進(jìn)行攔截 是否禁用事件攔截的功能(默認(rèn)是false),
// 可通過(guò)調(diào)用requestDisallowInterceptTouchEvent()修改
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//onInterceptTouchEvent 默認(rèn)返回 false,不攔截
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
//不攔截
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
//攔截
intercepted = true;
}
//沒(méi)有取消也沒(méi)有攔截,則分發(fā)給子View
if (!canceled && !intercepted) {
//如果 Touch 狀態(tài)為空并且子 View 個(gè)數(shù)不為 0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//循環(huán)查找
for (int i = childrenCount - 1; i >= 0; i--) {
//分發(fā)事件,記錄狀態(tài)
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
總結(jié)如下

View的事件分發(fā)機(jī)制
下面我們看源碼分析一下
public boolean dispatchTouchEvent(MotionEvent event) {
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//判斷是否注冊(cè) OnTouchListener
//判斷 OnTouchListener 中 onTouch 是否返回 true
//當(dāng) onTouch 返回 true 時(shí),事件被 onTouch 處理了,不會(huì)執(zhí)行 onTouchEvent 方法,說(shuō)明 onTouch 的優(yōu)先級(jí)高于 onTouchEvent
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
}
onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
//如果不能點(diǎn)擊則直接返回
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
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();
}
}
}
...
break;
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_CANCEL:
...
break;
case MotionEvent.ACTION_MOVE:
...
break;
}
}
...
}
View 事件分發(fā)總結(jié)
1.事件分發(fā)首先會(huì)調(diào)用dispatchTouchEvent()
2.事件分發(fā)執(zhí)行的順序?yàn)?onTouch -> onTouchEvent -> onClick,可以理解成 優(yōu)先級(jí) onTouch > onTouchEvent > onClick
3.當(dāng) OnTouchListener 為 true 時(shí),會(huì)消費(fèi)當(dāng)前事件,只會(huì)執(zhí)行 onTouch 方法,不會(huì)傳遞到 onTouchEvent 方法。當(dāng) OnTouchListener 為 false 時(shí),則會(huì)將事件傳遞
onClick 事件觸發(fā)的前提條件是可點(diǎn)擊的,onClick 觸發(fā)在 onTouchEvent 的UP 事件中