Android之事件分发机制
本文主要包括以下內(nèi)容
首先來(lái)看兩張圖
在執(zhí)行touch事件時(shí)
首先執(zhí)行dispatchTouchEvent方法,執(zhí)行事件分發(fā)。
再執(zhí)行onInterceptTouchEvent方法,判斷是否中斷事件,返回true時(shí)中斷,執(zhí)行自己的onTouchEvnet方法.
最后執(zhí)行onTouchEvent方法,處理事件
View的事件分發(fā)
不管是DOWN,MOVE,UP都會(huì)按照下面的順序執(zhí)行:
1、dispatchTouchEvent
2、 setOnTouchListener的onTouch
3、onTouchEvent
其中
如果我們?cè)O(shè)置了setOnTouchListener,并且return true,那么View自己的onTouchEvent就不會(huì)被執(zhí)行了
總結(jié)
1、整個(gè)View的事件轉(zhuǎn)發(fā)流程是:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中會(huì)進(jìn)行OnTouchListener的判斷,如果OnTouchListener不為null且返回true,則表示事件被消費(fèi),onTouchEvent不會(huì)被執(zhí)行;否則執(zhí)行onTouchEvent。
2、onTouchEvent中的DOWN,MOVE,UP
- DOWN時(shí):
a、首先設(shè)置標(biāo)志為PREPRESSED,設(shè)置mHasPerformedLongPress=false ;然后發(fā)出一個(gè)115ms后的mPendingCheckForTap;
b、如果115ms內(nèi)沒(méi)有觸發(fā)UP,則將標(biāo)志置為PRESSED,清除PREPRESSED標(biāo)志,同時(shí)發(fā)出一個(gè)延時(shí)為500-115ms的,檢測(cè)長(zhǎng)按任務(wù)消息;
c、如果500ms內(nèi)(從DOWN觸發(fā)開(kāi)始算),則會(huì)觸發(fā)LongClickListener:
此時(shí)如果LongClickListener不為null,則會(huì)執(zhí)行回調(diào),同時(shí)如果LongClickListener.onClick返回true,才把mHasPerformedLongPress設(shè)置為true;否則mHasPerformedLongPress依然為false;
MOVE時(shí):
主要就是檢測(cè)用戶(hù)是否劃出控件,如果劃出了:
115ms內(nèi),直接移除mPendingCheckForTap;
115ms后,則將標(biāo)志中的PRESSED去除,同時(shí)移除長(zhǎng)按的檢查:removeLongPressCallback();UP時(shí):
a、如果115ms內(nèi),觸發(fā)UP,此時(shí)標(biāo)志為PREPRESSED,則執(zhí)行UnsetPressedState,setPressed(false);會(huì)把setPress轉(zhuǎn)發(fā)下去,可以在View中復(fù)寫(xiě)dispatchSetPressed方法接收;
b、如果是115ms-500ms間,即長(zhǎng)按還未發(fā)生,則首先移除長(zhǎng)按檢測(cè),執(zhí)行onClick回調(diào);
c、如果是500ms以后,那么有兩種情況:
i.設(shè)置了onLongClickListener,且onLongClickListener.onClick返回true,則點(diǎn)擊事件OnClick事件無(wú)法觸發(fā);
ii.沒(méi)有設(shè)置onLongClickListener或者onLongClickListener.onClick返回false,則點(diǎn)擊事件OnClick事件依然可以觸發(fā);
d、最后執(zhí)行mUnsetPressedState.run(),將setPressed傳遞下去,然后將PRESSED標(biāo)識(shí)去除;
最后問(wèn)個(gè)問(wèn)題,然后再運(yùn)行個(gè)例子結(jié)束:
1、setOnLongClickListener和setOnClickListener是否只能執(zhí)行一個(gè)
不是的,只要setOnLongClickListener中的onClick返回false,則兩個(gè)都會(huì)執(zhí)行;返回true則會(huì)屏幕setOnClickListener
Android ViewGroup事件分發(fā)機(jī)制
大體的事件流程為:
MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent
可以看出,在View上觸發(fā)事件,最先捕獲到事件的為View所在的ViewGroup,然后才會(huì)到View自身~
1、ACTION_DOWN中,ViewGroup捕獲到事件,然后判斷是否攔截,如果沒(méi)有攔截,則找到包含當(dāng)前x,y坐標(biāo)的子View,賦值給mMotionTarget,然后調(diào)用 mMotionTarget.dispatchTouchEvent
2、ACTION_MOVE中,ViewGroup捕獲到事件,然后判斷是否攔截,如果沒(méi)有攔截,則直接調(diào)用mMotionTarget.dispatchTouchEvent(ev)
3、ACTION_UP中,ViewGroup捕獲到事件,然后判斷是否攔截,如果沒(méi)有攔截,則直接調(diào)用mMotionTarget.dispatchTouchEvent(ev)
當(dāng)然了在分發(fā)之前都會(huì)修改下坐標(biāo)系統(tǒng),把當(dāng)前的x,y分別減去child.left 和 child.top ,然后傳給child;
關(guān)于攔截
如何攔截
復(fù)寫(xiě)ViewGroup的onInterceptTouchEvent方法:
@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev){int action = ev.getAction();switch (action){case MotionEvent.ACTION_DOWN://如果你覺(jué)得需要攔截return true ; case MotionEvent.ACTION_MOVE://如果你覺(jué)得需要攔截return true ; case MotionEvent.ACTION_UP://如果你覺(jué)得需要攔截return true ; }return false;}默認(rèn)是不攔截的,即返回false;如果你需要攔截,只要return true就行了,這要該事件就不會(huì)往子View傳遞了,并且如果你在DOWN retrun true ,則DOWN,MOVE,UP子View都不會(huì)捕獲事件;如果你在MOVE return true , 則子View在MOVE和UP都不會(huì)捕獲事件。
原因很簡(jiǎn)單,當(dāng)onInterceptTouchEvent(ev) return true的時(shí)候,會(huì)把mMotionTarget 置為null ;
如何不被攔截
如果ViewGroup的onInterceptTouchEvent(ev) 當(dāng)ACTION_MOVE時(shí)return true ,即攔截了子View的MOVE以及UP事件;
此時(shí)子View希望依然能夠響應(yīng)MOVE和UP時(shí)該咋辦呢?
Android給我們提供了一個(gè)方法: requestDisallowInterceptTouchEvent(boolean) 用于設(shè)置是否允許攔截,我們?cè)谧覸iew的dispatchTouchEvent中直接這么寫(xiě):
getParent().requestDisallowInterceptTouchEvent(true); 這樣即使ViewGroup在MOVE的時(shí)候return true,子View依然可以捕獲到MOVE以及UP事件。
注
如果ViewGroup在onInterceptTouchEvent(ev) ACTION_DOWN里面直接return true了,那么子View是木有辦法的捕獲事件的~~~
如果沒(méi)有找到合適的子View
1、ACTION_DOWN的時(shí)候,子View.dispatchTouchEvent(ev)返回的為false ;
則不處理,向上傳遞,由父view處理
總結(jié)
關(guān)于代碼流程上面已經(jīng)總結(jié)過(guò)了~
1、如果ViewGroup找到了能夠處理該事件的View,則直接交給子View處理,自己的onTouchEvent不會(huì)被觸發(fā);
2、可以通過(guò)復(fù)寫(xiě)onInterceptTouchEvent(ev)方法,攔截子View的事件(即return true),把事件交給自己處理,則會(huì)執(zhí)行自己對(duì)應(yīng)的onTouchEvent方法
3、子View可以通過(guò)調(diào)用 getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup對(duì)其MOVE或者UP事件進(jìn)行攔截;
好了,那么實(shí)際應(yīng)用中能解決哪些問(wèn)題呢?
比如你需要寫(xiě)一個(gè)類(lèi)似slidingmenu的左側(cè)隱藏menu,主Activity上有個(gè)Button、ListView或者任何可以響應(yīng)點(diǎn)擊的View,你在當(dāng)前View上死命的滑動(dòng),菜單欄也出不來(lái);因?yàn)镸OVE事件被子View處理了~ 你需要這么做:在ViewGroup的dispatchTouchEvent中判斷用戶(hù)是不是想顯示菜單,如果是,則在onInterceptTouchEvent(ev)攔截子View的事件;自己進(jìn)行處理,這樣自己的onTouchEvent就可以順利展現(xiàn)出菜單欄了~
參考鏈接
Android View 事件分發(fā)機(jī)制 源碼解析 (上) - Hongyang - 博客頻道 - CSDN.NET
Android ViewGroup事件分發(fā)機(jī)制 - Hongyang - 博客頻道 - CSDN.NET
總結(jié)
以上是生活随笔為你收集整理的Android之事件分发机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 通过SVD求解单应矩阵
- 下一篇: Android之查看网络图片和网页HTM