前言
View 在 Android 中是一個(gè)非常重要的概念,作用堪比四大組件,所謂 View 的事件分發(fā),就是對(duì) MotionEvent 事件的分發(fā)過程,當(dāng)一個(gè) MotionEvent 產(chǎn)生了以后,系統(tǒng)需要把這個(gè)事件傳遞給一個(gè)具體的 View,這個(gè)過程就是事件分發(fā)
View 的雖稱不上 Android 四大組件,但它的重要性可以說是跟四大組件平級(jí),根據(jù)使用頻率,甚至比廣播跟內(nèi)容提供器重要;view 主要包含兩類:ViewGroup 和具體的 View,有 Android 開發(fā)經(jīng)驗(yàn)的都知道這兩類的區(qū)別了,我們時(shí)時(shí)刻刻都有使用到 view,例如: TextView、ImageView 等,正因?yàn)槲覀儠r(shí)時(shí)刻刻都在用,所以就顯得特別重要了
事件分發(fā)過程由三個(gè)很重要的方法共同來完成:public boolean dispatchTouchEvent(MotionEvent ev)public boolean onInterceptTouchEvent(MotionEvent ev)public boolean onTouchEvent(MotionEvent event)當(dāng)一個(gè)觸摸事件產(chǎn)生后,他的傳遞過程遵循如下順序:Activity->Window->View,即事件總是先傳遞給 Activity,Activity 再傳遞給 Window,最后再傳給頂級(jí) View,頂級(jí) View 接收到事件后,會(huì)按照分發(fā)機(jī)制向下繼續(xù)分發(fā)
順著箭頭走,可以看到事件是被
Activity 的 dispatchTouchEvent(MotionEvent ev)方法先拿到的,從方法名可以看出來這個(gè)方法就是分發(fā)觸摸事件的接著會(huì)調(diào)用 PhoneWindow 的 superDispatchTouchEvent(MotionEvent event)方法,其實(shí) PhoneWindow 是 Window 的唯一實(shí)現(xiàn)類再調(diào)用 DecorView 的 superDispatchTouchEvent(MotionEvent event)方法,DecorView 其實(shí)就是最頂級(jí)的 View,繼承自 FrameLayout,平時(shí)我們熟悉的 tContentView(R.layout.activity_layout)就是將布局設(shè)置到這個(gè) DecorView接著來到 ViewGroup ,這個(gè) ViewGroup 就是我們平時(shí)自己布局里面的最外層的父布局例如: LinearLayout,事件來到這里就會(huì)稍微復(fù)雜起來了,會(huì)根據(jù)情況處理事件
首先還是調(diào)用 ViewGroup 的 dispatchTouchEvent(MotionEvent ev)方法,在該方法會(huì)優(yōu)先判斷 disallowIntercept 這個(gè)布爾值,這個(gè)disallowIntercept后面會(huì)講到如果為 true,則說明 ViewGroup 不消費(fèi)事件,事件就來到了子 View 的 dispatchTouchEvent(MotionEvent event),這里假設(shè)我們的布局是很簡單的,一個(gè) LinearLayout 包含一個(gè) Button,這樣方面分析,所以這個(gè)子View就是 Button,如果有設(shè)置 mOnTouchListener,這事件會(huì)來到mOnTouchListener.onTouch(this, event)如果 onTouch 方法返回 true 則該事件就被消費(fèi)了,返回fal的話事件會(huì)來到 onTouchEvent(event)方法,在該方法會(huì)判斷 mOnClickListener 是否被設(shè)置,這個(gè)監(jiān)聽器是我們平時(shí)用的最多的 tOnClickListener(@Nullable OnClickListener l)方法,這個(gè)方法的優(yōu)先級(jí)從這里可以看出優(yōu)先級(jí)是最低的,因?yàn)槭录阶詈蟛艜?huì)來到這里如果 onTouchEvent(event)返回 fal;則會(huì)調(diào)用 ViewGroup 的 onTouchEvent(ev)如果 ViewGroup 的 onTouchEvent(ev)還是返回 fal,則事件最終丟回給 Activity 的 onTouchEvent(ev) 方法了,相當(dāng)于這個(gè)事件走了地球一圈都沒人要還是回到了原點(diǎn)剛剛分析到 ViewGroup 的時(shí)候,當(dāng) disallowIntercept 為 fal 時(shí),事件會(huì)來到它的 onInterceptTouchEvent(ev),這個(gè)方法的從名字可以看出是否攔截事件的意思,返回 true 表示攔截,則會(huì)交給自己的 onTouchEvent(ev)方法,返回 fal 表示不攔截事件,將事件丟給了 View 的 dispatchTouchEvent(MotionEvent event),也就相當(dāng)于 disallowIntercept 等于 true
前面 ViewGroup 說到的 disallowIntercept 變量是通過 parent.requestDisallowInterceptTouchEvent(boolean)設(shè)置的,設(shè)置是否禁止攔截事件的意思,一般子 view 不希望父類攔截事件的話可以調(diào)用該方法,在處理滑動(dòng)沖突的時(shí)候會(huì)經(jīng)常用到這個(gè)方法,他的優(yōu)先級(jí)也是最高的
接下來用代碼是示例:Activity 對(duì)事件的分發(fā)public boolean dispatchTouchEvent(MotionEvent ev) { ... if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
Activity 的事件的處理其實(shí)并不負(fù)責(zé),即如果下層(不管是 ViewGroup 還是 View )消耗了這個(gè)事件,那么 if 語句就為 true , 則 dispatchTouchEvent 就返回 true ;如果沒有消耗就自己對(duì)事件進(jìn)行處理,即調(diào)用 onTouchEvent 方法
public boolean onTouchEvent(MotionEvent event) { if (mWindow.shouldCloOnTouch(this, event)) { finish(); return true; } return fal; }/** @hide */ public boolean shouldCloOnTouch(Context context, MotionEvent event) { final boolean isOutside = event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event) || event.getAction() == MotionEvent.ACTION_OUTSIDE; if (mCloOnTouchOutside && peekDecorView() != null && isOutside) { return true; } return fal; }
Activity 的 onTouchEvent 會(huì)對(duì)這個(gè)事件進(jìn)行判斷,如果事件在窗口邊界外就返回 true,dispatchTouchEvent 就返回 true ;如果在邊界內(nèi)就 返回 fal ,最后 dispatchTouchEvent 也會(huì)返回 fal
View 對(duì)事件的分發(fā)這里先說 View 對(duì)事件的分發(fā)是因?yàn)?ViewGroup 繼承自 View ,ViewGroup 對(duì)事件的分發(fā)會(huì)調(diào)用到父類(也就是View )的方法,因此先理清 View 的分發(fā)有助于理解
public boolean dispatchTouchEvent(MotionEvent event) { ... 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; } ... return result; }
可以看到 View 的事件的處理是先判斷 mOnTouchListener !=null 和 View 設(shè)置 ENABLED 這兩個(gè)條件成不成立,不過成立則 調(diào)用 onTouch 方法,且如果 onTouch 返回了 true ,那個(gè)事件就被消耗 ,View 的 dispatchTouchEvent 就返回 true ; 相反,如果條件不成立或者 onTouch 返回 fal ,那么就會(huì)執(zhí)行 View 的 onTouchEvent 方法
public boolean onTouchEvent(MotionEvent event) { ... final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; // 若可點(diǎn)擊,包括LONG_CLICKABLE 或者 CLICKABLE if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { ca MotionEvent.ACTION_UP: boolean prepresd = (mPrivateFlags & PREPRESSED) != 0; ... // 執(zhí)行performClick() performClick(); break; ca MotionEvent.ACTION_DOWN: ... break; ca MotionEvent.ACTION_CANCEL: ... break; ca MotionEvent.ACTION_MOVE: ... break; } //>> 若可點(diǎn)擊,就返回true return true; } //>> 若不可點(diǎn)擊,就返回fal return fal; }public boolean performClick() { if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; } return fal; }
在 onTouchEvent 方法中,如果 View 是可點(diǎn)擊的,比如設(shè)置了 onClick 或者 onLongClick ,就會(huì)執(zhí)行 onClick 方法,并且 onTouchEvent 返回 true
如果是不可點(diǎn)擊的就返回 fal 。需要注意的是這里的 onTouchEvent 是可以被重寫的如果 onTouchEvent 返回 true 那么 View 的 dispatchTouchEvent 就返回 true ,事件就被消耗如果 onTouchEvent 返回 fal , 那么 dispatchTouchEvent 也返回 fal ,這時(shí) 事件就交由上層處理,也就是 ViewGroup小結(jié)這篇文章,其實(shí)不難;主要是將 View 的事件分發(fā)機(jī)制以及開發(fā)當(dāng)中經(jīng)常用到的一些知識(shí)點(diǎn),總結(jié)了一下
最后這里放上我耗時(shí)兩個(gè)月,將自己8年 Android 開發(fā)的知識(shí)筆記整理成了一份系統(tǒng)學(xué)習(xí)資料筆記,技術(shù)相關(guān)的知識(shí)點(diǎn)在筆記中都有詳細(xì)的解讀并且把每個(gè)技術(shù)點(diǎn)整理成了 PDF 文檔(知識(shí)脈絡(luò) + 諸多細(xì)節(jié))有需要的小伙伴:可以私信發(fā)送"筆記"就可以免費(fèi)領(lǐng)取了
BAT 高工必修- View 繪制BAT 高工必修- View 事件分發(fā)機(jī)制以上就是全套大廠學(xué)習(xí)筆記面試指南,吃透一半保你可以吊打面試官,只有自己真正強(qiáng)大了,有了核心競(jìng)爭(zhēng)力,你才有拒絕offer的權(quán)力,所以,奮斗吧!開發(fā)者們!千里之行,始于足下;種下一顆樹最好的時(shí)間是十年前,其次,就是現(xiàn)在
本文發(fā)布于:2023-02-28 20:13:00,感謝您對(duì)本站的認(rèn)可!
本文鏈接:http://m.newhan.cn/zhishi/a/167766362679112.html
版權(quán)聲明:本站內(nèi)容均來自互聯(lián)網(wǎng),僅供演示用,請(qǐng)勿用于商業(yè)和其他非法用途。如果侵犯了您的權(quán)益請(qǐng)與我們聯(lián)系,我們將在24小時(shí)內(nèi)刪除。
本文word下載地址:motionevent(MotionEvent.obtain).doc
本文 PDF 下載地址:motionevent(MotionEvent.obtain).pdf
| 留言與評(píng)論(共有 0 條評(píng)論) |