ViewGroup事件傳遞
上篇筆記中介紹道埋心,ViewGroup中參與事件傳遞的方法有以下三個(gè):
public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent ev)
ViewGroup事件傳遞方法調(diào)用
dispatchTouchEvent
當(dāng)我們點(diǎn)擊控件時(shí)广凸,首先調(diào)用的是布局組件(ViewGroup)的dispatchTouchEvent方法。這個(gè)方法和View中的不太一樣,它需要去確定用戶(hù)到底點(diǎn)擊的是哪個(gè)子View载慈,并將事件分發(fā)給他毅桃。然后調(diào)用這個(gè)子View的dispatchTouchEvent,最后按照上篇筆記中記錄的流程翁狐,執(zhí)行事件的分發(fā)类溢。ViewGroup.dispatchTouchEvent()部分源代碼如下:
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
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;
}
上述代碼中,有個(gè)比較重要的局部變量:intercepted谴蔑。這個(gè)變量決定了ViewGroup是否將事件分發(fā)給子View豌骏。而在代碼中我們可以看到,這個(gè)變量的值就是onInterceptTouchEvent()方法的返回值隐锭。接下來(lái)我們看onInterceptTouchEvent()方法.
onInterceptTouchEvent
onInterceptTouchEvent源代碼:
Public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
這個(gè)方法的代碼及其簡(jiǎn)單窃躲。當(dāng)該方法返回true時(shí),即事件交給ViewGroup自身來(lái)消費(fèi)钦睡,后續(xù)調(diào)用ViewGroup的onTouch和onTouchEvent方法蒂窒;當(dāng)返回false時(shí),事件將分發(fā)給子View來(lái)觸發(fā)。
當(dāng)事件交由子View來(lái)觸發(fā)時(shí)洒琢,那么ViewGroup.diapatchTouchEvent方法的返回值完全有子View的diapatchTouchEvent返回值決定秧秉。如果子View是可點(diǎn)擊的,那么ViewGroup的diapatchTouchEvent返回true衰抑,也就是代表事件已經(jīng)被子View消費(fèi)掉了象迎,即不會(huì)執(zhí)行ViewGroup的onTouch和onTouchEvent方法。如果子View沒(méi)有消費(fèi)事件呛踊,那么會(huì)調(diào)用ViewGroup的父類(lèi)的diapatchTouchEvent方法砾淌,進(jìn)行事件分發(fā),交由onTouch和onTouchEvent觸發(fā)谭网。當(dāng)我們點(diǎn)擊ViewGroup的空白區(qū)域汪厨,同樣會(huì)調(diào)用父類(lèi)的diapatchTouchEvent進(jìn)行事件分發(fā)。
實(shí)踐
- 當(dāng)事件正常從ViewGroup分發(fā)到對(duì)應(yīng)的子View愉择,log打印如下:
2.當(dāng)事件在ViewGroup中被攔截劫乱,onInterceptTouchEvent方法返回true,Log打印如下:
3.當(dāng)點(diǎn)擊了ViewGroup中不可點(diǎn)擊的子View锥涕,Log打印如下:
Button要設(shè)置成不可點(diǎn)擊狀態(tài)衷戈,只能通過(guò)將Button控件的enable屬性設(shè)置為false。
事件攔截
事件傳遞方法返回值都是boolean型站楚,true代表事件已經(jīng)被消費(fèi)脱惰,false代表事件沒(méi)有被消費(fèi),可以分發(fā)窿春。
onTouch方法返回true
onTouch方法默認(rèn)返回值都是false拉一,當(dāng)我們使其返回true,將事件攔截在了touch事件旧乞。也就是說(shuō)在dispatchTouchEvent方法中不會(huì)調(diào)用onTouchEvent方法(觸發(fā)點(diǎn)擊事件在該方法中)蔚润。
onInterceptTouchEvent方法返回true
onInterceptTouchEvent方法默認(rèn)返回是false,當(dāng)我們將其返回值設(shè)定為true時(shí)尺栖,也就是讓事件不能分發(fā)給ViewGroup的子View嫡纠。讓ViewGroup自身來(lái)消費(fèi)事件。
Activity觸摸事件傳遞
其實(shí)延赌,當(dāng)我們觸摸屏幕時(shí)除盏,事件傳遞的順序應(yīng)該是activity->viewgroup->view。和VIewGroup挫以,View相同者蠕,Activity也有dispatchTouchEvent和onTouchEvent方法。觸摸事件最先調(diào)用的是Activity的dispatchTouchEvent方法,代碼如下:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
首先判斷當(dāng)前事件是否是ACTION_DOWN掐松,如果是調(diào)用onUserInteraction()方法踱侣。該方法可以在Activity中被重寫(xiě)粪小,在事件被分發(fā)前會(huì)調(diào)用該方法。該方法的返回值是void型抡句,不會(huì)對(duì)事件傳遞結(jié)果造成影響探膊。接著會(huì)判斷getWindow().superDispatchTouchEvent(ev)的執(zhí)行結(jié)果。這一步首先通過(guò)getWindow()方法得到Activity被加載時(shí)待榔,創(chuàng)建的PhoneWindow對(duì)象逞壁,然后調(diào)用PhoneWindow對(duì)象中的DecorView成員變量的dispatchTouchEvent方法對(duì)事件進(jìn)行分發(fā)。如果事件沒(méi)有被DecorView及其子View消費(fèi)究抓,那么調(diào)用Activity的onTouchEvent()方法消費(fèi)事件猾担。