前言
· 在Android自定義View/ViewGroup中,我們免不了出現(xiàn)它的觸摸事件,所以了解View的事件分發(fā)非常重要
· 本人菜雞水平,寫(xiě)此文章為了加深印象,如果發(fā)現(xiàn)錯(cuò)誤或不足懇請(qǐng)指正。謝謝
講在前面
在事件分發(fā)過(guò)程中,我們首先要知道View的事件分發(fā)是要區(qū)分View和ViewGroup

View的事件分發(fā)
view事件分發(fā)中的三個(gè)重要方法:
1.dispatchTouchEvent()
2.onTouch()
3.onTouchEvent()
·我看首先看看事件在View是如何傳遞和消費(fèi)的
DispatchTouchEvent()部分代碼,也是核心代碼
boolean result = false;
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;
}
首先會(huì)判斷View是否設(shè)置了觸摸事件,如果設(shè)置了點(diǎn)擊事件,并且并且當(dāng)前View是可點(diǎn)擊的,那么就會(huì)回調(diào)li.mOnTouchListener.onTouch(this, event)),如果此方法返回true,那么就不會(huì)調(diào)用onTouchEvent(event)這個(gè)方法了,說(shuō)明這個(gè)觸摸事件被消費(fèi)了。如果返回false,那么就會(huì)回調(diào)onTouchEvent(event)這個(gè)方法,如果返回true,事件被銷毀。返回false代表事件繼續(xù)向下傳遞。由此可見(jiàn)執(zhí)行優(yōu)先級(jí):onTouch() >onTouchEvent()。
然后我們看看onTouchEvent()這個(gè)方法
是switch case語(yǔ)句我們不分析,各種事件了。我們主要看看UP中:
case MotionEvent.ACTION_UP:
//......
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
//......
break;
我們?cè)诖蜷_(kāi)performClickInternal看看,最后就是performClick()這個(gè)方法
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;
}
通過(guò)源碼我們可以看到,onClickListener.onClick()方法其實(shí)就是onTouchEvent中up事件中被調(diào)用,所以執(zhí)行優(yōu)先級(jí):onTouchEvent() > onClick()。如果你在執(zhí)行ACTION_DOWN的時(shí)候返回了false,后面一系列其它的action就不會(huì)再得到執(zhí)行了。簡(jiǎn)單的說(shuō),就是當(dāng)dispatchTouchEvent在進(jìn)行事件分發(fā)的時(shí)候,只有前一個(gè)action返回true,才會(huì)觸發(fā)后一個(gè)action。View的事件分發(fā)就說(shuō)到這。
ViewGroup的事件分發(fā)
view事件分發(fā)中的三個(gè)重要方法:
1.dispatchTouchEvent()
2.onInterceptTouchEvent()
3.onTouchEvent()
首先我們看看ViewGroup的dispatchTouchEvent()這個(gè)方法,源碼太多,我們把核心的代碼提出來(lái)
public boolean dispatchTouchEvent(MotionEvent ev){
//......
boolean handled = false;
final boolean intercepted;
intercepted=onInterceptTouchEvent(event);
if(intercepted)
handled =onTouchEvent(event)
else{
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) //找到當(dāng)前觸摸位置的childView
handled = child.dispatchTouchEvent(event);
}
//......
return handled;
}
這個(gè)就是ViewGroup的邏輯,首先先判斷onInterceptTouchEvent(event)(默認(rèn)為false)如果為true,則攔截這個(gè)事件。調(diào)用viewgroup的onTouchEvent(event)來(lái)判斷是否消費(fèi)事件。否則向下傳遞。遍歷找到子View并把當(dāng)前事件交給子View調(diào)用子View的dispatchTouchEvent(event)。
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
onInterceptTouchEvent()默認(rèn)返回false,代表viewGroup不攔截事件
1.當(dāng)事件傳遞到View時(shí),如果View的onTouchEvent()返回false,則父類的onTouchEvent()會(huì)被調(diào)用,依次向上傳遞
2.若所有的View都不消耗事件時(shí),Activity的onTouchEvent()會(huì)被調(diào)用
結(jié)論
1.對(duì)于View一旦決定攔截事件即onTouchEvent()返回true,那后續(xù)的整個(gè)事件序列都會(huì)交給它消耗;
2.執(zhí)行優(yōu)先級(jí):onTouch() > onTouchEvent() > onClick()
3.事件的傳遞是
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
如果沒(méi)有找到合適的View消費(fèi)事件:那么事件反向傳遞如果最后 Activity 也沒(méi)有處理,本次事件才會(huì)被拋棄
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View
感謝閱讀,歡迎點(diǎn)贊和評(píng)論