Android View事件分發(fā)和消費(fèi)源碼簡單理解
Android View事件分發(fā)和消費(fèi)源碼簡單理解
前言:
開發(fā)過程中覺得View事件這塊是特別燒腦的,看了好久,才自認(rèn)為看明白。中間上網(wǎng)查了下singwhatiwanna粉絲的讀書筆記,有種茅塞頓開的感覺。
很重要的學(xué)習(xí)方法:化繁為簡,只抓重點(diǎn)。
源碼一坨,不要指望每一行代碼都看懂。首先是沒必要,其次大量非關(guān)鍵代碼會(huì)讓你模糊真正重要的部分。
以下也只是學(xué)姐的學(xué)習(xí)成果,各位同學(xué)要想理解深刻,還需要自己親自去看源碼。
2.源碼分析
由于源碼實(shí)在太長,而且也不容易看懂,學(xué)姐這里就不貼出來了,因?yàn)闆]必要。
以下是學(xué)姐簡化版源碼。
(1)ViewGroup.dispatchTouchEvent(event)
boolean dispatchTouchEvent(MotionEvent event) { int action = event.getAction(); //判斷ViewGroup是否攔截touch事件。當(dāng)為ACTION_DOWN或者找到能夠接收touch事件的子View 時(shí),由onInterceptTouchEvent(event)決定是否攔截。其他情況,即ACTION_MOVE/ACTION_UP且 沒找到能夠接收touch事件的子View時(shí),直接攔截。 boolean intercepted; if (action == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { intercepted = onInterceptTouchEvent(event); } else { intercepted = true; } //如果ViewGroup不攔截touch事件。在ACTION_DOWN時(shí)遍歷所有子View,查找能夠接收touch事件的 子View。如果找到則設(shè)置mFirstTouchTarget,并跳出循環(huán)。 boolean alreadyDispatchedToNewTouchTarget = false; if (!intercepted) { if (action == MotionEvent.ACTION_DOWN) { for (int i = childrenCount - 1; i >= 0; i--) { if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } if (dispatchTransformedTouchEvent(event, child)) { //找到mFirstTouchTarget newTouchTarget = addTouchTarget(child); alreadyDispatchedToNewTouchTarget = true; break; } } } } //事件下發(fā)及消費(fèi)。如果沒找到能夠接收touch事件的子View,則由ViewGroup自己處理及消費(fèi)。 如果找到能夠接收touch事件的子View,則由子View遞歸處理touch事件及消費(fèi)。 boolean handled = false; if (mFirstTouchTarget == null) { handled = dispatchTransformedTouchEvent(event, null); } else { if (alreadyDispatchedToNewTouchTarget) { handled = true; } else { while (touchTarget) { handled = dispatchTransformedTouchEvent(event, child); } } } return handled; } //ViewGroup事件下發(fā)。如果無接收touch事件的子View,則由ViewGroup的父類(即View)下發(fā)touch事件 如果child非空,則交由子View下發(fā)touch事件,子View可以是ViewGroup或View。 boolean dispatchTransformedTouchEvent(MotionEvent event, View child) { boolean handled; if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } return handled; }
(2)View.dispatchTouchEvent(event)
//View的Touch事件分發(fā)。當(dāng)外部設(shè)置了mOnTouchListener時(shí),先交由mOnTouchListener.onTouch(event)消費(fèi)。 若未消費(fèi),則交給View的onTouchEvent(event)消費(fèi)。onTouchEvent的實(shí)現(xiàn)是,如果設(shè)置了mOnClickListener, 則執(zhí)行mOnClickListener.onClick()點(diǎn)擊事件。返回值為true,表示消費(fèi),否則未消費(fèi)。 boolean dispatchTouchEvent(MotionEvent event) { boolean result = false; if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } return result; } boolean onTouchEvent(MotionEvent event) { performClick(); }
3.總結(jié)
總結(jié)下ViewGroup的事件分發(fā)及消費(fèi)過程:
整個(gè)過程包括3個(gè)部分:判斷是否攔截 -> 查找接收touch事件的子View -> 事件下發(fā)及消費(fèi)
判斷是否攔截:
(1) ACTION_DOWN 或者 非ACTION_DOWN且找到接收touch事件的子View時(shí),由onInterceptTouchEvent(event)決定是否攔截
(2) 非ACTION_DOWN,且未找到接收touch事件的子View時(shí),標(biāo)明需要攔截touch事件
這里解釋下,影響ViewGroup是否能攔截touch事件有2個(gè)因素:是否 找到了接收touch事件的子View 和 onInterceptTouchEvent(event). 而查找接收touch事件的子View這一過程只需要在ACTION_DOWN的時(shí)候確定好就行。如果ACTION_DOWN的時(shí)候沒找到,那么ACTION_MOVE和ACTION_UP肯定也找不到,因此touch事件直接被ViewGroup攔截。如果找到了接收touch事件的子View,那么ACTION_MOVE和ACTION_UP情況下還是要檢查下ViewGroup的onInterceptTouchEvent(event),看下是否攔截。
查找接收touch事件的子View:
(1) 兩種情況下查找:ACTION_DOWN且ViewGroup不攔截的情況下。
(2) 查找方法:遍歷所有子View,如果touch事件的xy坐標(biāo)在該ViewGroup的某個(gè)子View范圍內(nèi),則針對(duì)該子View執(zhí)行遞歸分發(fā)touch事件操作,如果找到有子View處理touch事件(return true),則跳出循環(huán)。
這里解釋下查找條件。查找接收touch事件的子View,顯然只需要ACTION_DOWN情況下即可,沒必要ACTION_MOVE和ACTION_UP都檢查,否則重復(fù)操作。如果ViewGroup都已經(jīng)攔截了,顯然不需要再去考慮子View怎么樣了。
事件下發(fā)及消費(fèi):
(1)兩種情況:ViewGroup下發(fā)及消費(fèi) 或者 ViewGroup的子View下發(fā)及消費(fèi)
(2)如果經(jīng)過以上兩步,沒找到接收Touch事件的子View,那么由ViewGroup進(jìn)行下發(fā)及消費(fèi),下發(fā)及調(diào)用流程是:ViewGroup.dispatchTouchEvent -> View.dispatchTouchEvent -> mOnTouchListener.onTouch -> onTouchEvent -> onClick
(3)如果找到接收touch事件的子View,則針對(duì)該子View執(zhí)行touch事件遞歸下發(fā)及消費(fèi)的操作
補(bǔ)充:
(1) 源碼中,mFirstTouchEvent表示接收touch事件的子View
(2) 步驟2和3,都有執(zhí)行dispatchTransformedTouchEvent(event, child)的操作,步驟2中只是為了查找接收touch事件的子View,步驟3主要目的是進(jìn)行事件分發(fā)及消費(fèi)。如果步驟2中針對(duì)某個(gè)子View已經(jīng)執(zhí)行了該方法,則步驟3中不再重復(fù)執(zhí)行。個(gè)人理解,不知道是否有誤。
4.結(jié)論
(1) 回調(diào)方法
ViewGroup:dispatchTouchEvent -> onInterceptTouchEvent -> onTouchEvent
View: dispatchTouchEvent -> onTouch
(2) 調(diào)用順序
Action執(zhí)行順序:ACTION_DOWN -> ACTION_MOVE -> ACTION_UP
ViewGroup: dispatchTouchEvent -> onInterceptTouchEvent -> onTouchEvent()
View: dispatchTouchEvent -> onTouchEvent
事件分發(fā)傳遞順序: Parent View -> Child View
ViewGroup1.dispatchTouchEvent -> ViewGroup2.dispatchTouchEvent
-> View3.dispatchTouchEvent
(緊跟著是View3.onTouchEvent)
事件消費(fèi)傳遞順序:Child View -> Parent View
View3.onTouchEvent -> ViewGroup2.onTouchEvent
-> ViewGroup1.onTouchEvent
個(gè)人理解這種傳遞順序,是由dispatchTransformedTouchEvent引起的,這里就是遞歸調(diào)用,整個(gè)事件的入口就是ViewGroup.dispatchTouchEvent.
以上就是Android View事件分發(fā)和消費(fèi)源碼的文章分享,關(guān)于Android view事件的分發(fā)機(jī)制大家可以在本站搜索相應(yīng)的文章進(jìn)行擴(kuò)展學(xué)習(xí)!
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android面試Intent采用了什么設(shè)計(jì)模式解析
這篇文章主要為大家介紹了Android面試Intent采用了什么設(shè)計(jì)模式解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Android利用ViewPager實(shí)現(xiàn)可滑動(dòng)放大縮小畫廊效果
這篇文章主要介紹了Android利用ViewPager實(shí)現(xiàn)可滑動(dòng)放大縮小畫廊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08Android學(xué)習(xí)之Span的使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android中各種Span類的使用方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定的幫助,需要的可以參考一下2022-06-06Android利用Glide獲取圖片真正的寬高的實(shí)例
本篇文章主要介紹了Android利用Glide獲取圖片真正的寬高的實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08Android編程實(shí)現(xiàn)畫板功能的方法總結(jié)【附源碼下載】
這篇文章主要介紹了Android編程實(shí)現(xiàn)畫板功能的方法,結(jié)合實(shí)例形式總結(jié)分析了Android基于自定義View與Canvas類實(shí)現(xiàn)畫板功能的具體操作步驟與相關(guān)注意事項(xiàng),需要的朋友可以參考下2018-02-02Android下保存簡單網(wǎng)頁到本地(包括簡單圖片鏈接轉(zhuǎn)換)實(shí)現(xiàn)代碼
這篇文章主要介紹了Android下保存簡單網(wǎng)頁到本地(包括簡單圖片鏈接轉(zhuǎn)換)實(shí)現(xiàn)代碼,需要的朋友可以參考下2014-02-02Android仿QQ個(gè)人標(biāo)簽添加與刪除功能
這篇文章主要為大家詳細(xì)介紹了Android仿QQ個(gè)人標(biāo)簽添加與刪除功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android View實(shí)現(xiàn)圓形進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android View實(shí)現(xiàn)圓形進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08