午夜无码人妻aⅴ大片色欲张津瑜,国产69久久久欧美黑人A片,色妺妺视频网,久久久久国产综合AV天堂

android的焦點事件,android 焦點

Android TV按鍵焦點原理淺談

原文鏈接: Android TV按鍵焦點原理淺談

成都創(chuàng)新互聯(lián)是專業(yè)的甘德網(wǎng)站建設(shè)公司,甘德接單;提供成都做網(wǎng)站、成都網(wǎng)站建設(shè),網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行甘德網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊,希望更多企業(yè)前來合作!

本篇主要閱讀 Android 源碼講解 TV 的按鍵事件分發(fā)原理和焦點查找原理,源碼基于 Android9.0 ,首先思考幾個問題:

帶著這些問題,我們一起來擼 Android 源碼吧!了解了系統(tǒng)是如何處理的有便于我們解決 TV 上一些按鍵和焦點的問題。

首先我們看下按鍵事件的入口 ViewRootImpl 類中的 ViewPostImeInputStage 內(nèi)部類:

可以看到注釋1,2,3,4分別判斷不同事件執(zhí)行不同方法,本篇主要討論的TV焦點事件,主要看下 processKeyEvent 方法:

可以看到在該方法中執(zhí)行了 mView.dispatchKeyEvent 方法,這里的 View 其實是 DecorView ,接著看下該方法:

上面首先判斷了如果是第一次按下則處理panel的快捷鍵,如果處理了則不往下走,否則繼續(xù)判斷當(dāng)窗口未銷毀且回調(diào)非空則回調(diào)處理,如果處理了則不往下走,否則讓 PhoneWindow 對應(yīng)的 onKeyDown , onKeyUp 方法來處理。

接下來我們按照這個派發(fā)順序依次來看看相關(guān)方法的實現(xiàn),這里先看看 Activity 的 dispatchKeyEvent 實現(xiàn):

我們看第1點 superDispatchKeyEvent 方法,可以看到該方法為一個抽象方法,而它的實現(xiàn)是實現(xiàn)它的子類 PhoneWindow :

該方法又回調(diào)用 DecorView 中的 superDispatchKeyEvent 方法:

此時,再來看下 ViewGroup 的 dispatchKeyEvent 方法:

接著看下 View 的 dispatchKeyEvent 方法:

該方法主要是判斷如果有給 View 設(shè)置 OnKeyListener 事件且 View 為可用狀態(tài),則優(yōu)先處理監(jiān)聽事件,其次調(diào)用 KeyEvent 的 dispatch 方法,接下來我們看下該方法:

該方法主要處理了按下、彈起事件,其中按下如果 mRepeatCount 重復(fù)次數(shù)大于0判斷為長按,則執(zhí)行長按事件。

我們繼續(xù)看下 View 的 onKeyDown 方法:

按下事件判斷了如果為確認(rèn)相關(guān)的按鍵才到下一步處理,判斷點擊或長按條件滿足,執(zhí)行按下 View 正中心坐標(biāo),然后執(zhí)行 checkForLongClick 檢查長按方法,看下該方法如下:

我們經(jīng)常會遇到電視按遙控器時長按會執(zhí)行一次 onKeyDown 、 onKeyUp ,之后才是一直 onKeyDown ,松開后才執(zhí)行 onKeyUp ,原因就在于這個檢查長按方法是延遲的。 delayOffset 傳進(jìn)來的是0,所以延遲時間為 ViewConfiguration.getLongPressTimeout() ,即該類中定義的 DEFAULT_LONG_PRESS_TIMEOUT 常量。

同樣的如果是觸摸屏,可以看下 View 類中的 onTouchEvent 方法在按下操作的時候會開啟 CheckForTap 線程檢查是否是長按,該線程同樣是延遲的,時間為 ViewConfiguration.getTapTimeout() ,即該類中的 TAP_TIMEOUT 常量,知道了這個你就知道如果寫腳本或插件模擬長按應(yīng)該間隔多長時間了,是不是一下你的模擬長按插件速度又可以更加準(zhǔn)確快速的實現(xiàn)了。

不同版本系統(tǒng)定義的延遲時間有可能不一樣,比如Google API 28 的 DEFAULT_LONG_PRESS_TIMEOUT 是500, TAP_TIMEOUT 是100,而 API 30 的 DEFAULT_LONG_PRESS_TIMEOUT 是400, TAP_TIMEOUT 也是100。

接下來再看下 Activity 的 onKeyDown :

回到 Decorview 中的 dispatchKeyEvent 方法看看 PhoneWindow 的 onKeyDown 方法:

onKeyUp 方法也可以自己再看下,以上就是淺談按鍵事件的分發(fā)流程了。

總結(jié):

上面講解了按鍵事件分發(fā)流程,當(dāng)上面分發(fā)完所有都沒消費的時候,就會繼續(xù)走 ViewRootImpl 的焦點導(dǎo)航流程,接下來看下 performFocusNavigation 方法:

首先我們看 mView.findFocus() ,該方法實際是調(diào)用了 ViewGroup 的 findFocus 方法:

該方法很簡單,就是向下遞歸查找在當(dāng)前頁面已經(jīng)獲取焦點的 View ,繼續(xù)看 focused.focusSearch(direction) 調(diào)用了 View 的 focusSearch 方法:

該方法向上遞歸查找,調(diào)用 ViewGroup 的 focusSearch 方法:

如果是根命名空間,則調(diào)用 FocusFinder 的 findNextFocus 方法查找焦點,否則繼續(xù)往上查找。繼續(xù)看 FocusFinder 的 findNextFocus 方法:

可以看到該方法首先查找用戶指定的下一個獲取焦點的 view ,如果找到了直接返回該 view ,如果沒找到繼續(xù)下面先添加 effectiveRoot 下的所有 view 到 focusables 集合中去,然后調(diào)用 findNextFocus 方法查找系統(tǒng)可獲取下一個焦點的最近 view 。

我們先看下 findNextUserSpecifiedFocus 方法的實現(xiàn):

通過用戶指定焦點方式不是本篇的重點,這里就不貼出內(nèi)部細(xì)節(jié)源碼了。該方法實際就是調(diào)用 View 的 findUserSetNextFocus 方法來查找用戶設(shè)置的下一個可獲取焦點的 view ,然后在 while 循環(huán)中判斷如果找到的是可以獲取焦點并且可見的并且不是 InTouchNode 模式,則返回該焦點,否則繼續(xù)循環(huán)查找直到找了一個循環(huán)沒有找到可以獲取焦點的或者 userSetNextFocus 為 null 跳出循環(huán)返回 null 。

再來看下系統(tǒng)就近原則查找的 findNextFocus 方法:

該方法主要通過 findNextFocusInRelativeDirection 在相對方向上找下一個焦點,該方法內(nèi)部邏輯比較簡單,這里就不貼出來了,進(jìn)去看下就知道其實就是先給 focusables 排序,然后從中找到 focused 在其中的后一個或前一個 view ,如果沒找到并且 focusables 不為空則返回 focusables 的第一個。

接下來我們重點看下 findNextFocusInAbsoluteDirection 方法:

再看下 isBetterCandidate 方法,該方法很關(guān)鍵,內(nèi)部包含一系列邏輯如何成為最佳候選者:

該方法英文注釋很直觀,就不中文翻譯了,首先看下成為候選人的 isCandidate 方法:

該方法判斷了目標(biāo)Rect如果在源Rect的方向一側(cè)且不在內(nèi)部的話,則為候選者,如第一個 destRect 左側(cè)應(yīng)在 srcRect 左側(cè)左邊, destRect 右側(cè)應(yīng)在 srcRect 右側(cè)左邊,其他方向同理。

接下來看下 beamBeats 方法:

Android TV 焦點原理源碼解析

相信很多剛接觸AndroidTV開發(fā)的開發(fā)者,都會被各種焦點問題給折磨的不行。不管是學(xué)技術(shù)還是學(xué)習(xí)其他知識,都要學(xué)習(xí)和理解其中原理,碰到問題我們才能得心應(yīng)手。下面就來探一探Android的焦點分發(fā)的過程。

Android焦點事件的分發(fā)是從ViewRootImpl的processKeyEvent開始的,源碼如下:

源碼比較長,下面我就慢慢來講解一下具體的每一個細(xì)節(jié)。

dispatchKeyEvent方法返回true代表焦點事件被消費了。

ViewGroup的dispatchKeyEvent()方法的源碼如下:

(2)ViewGroup的dispatchKeyEvent執(zhí)行流程

(3)下面再來瞧瞧view的dispatchKeyEvent方法的具體的執(zhí)行過程

驚奇的發(fā)現(xiàn)執(zhí)行了onKeyListener中的onKey方法,如果onKey方法返回true,那么dispatchKeyEvent方法也會返回true

可以得出結(jié)論:如果想要修改ViewGroup焦點事件的分發(fā),可以這么干:

注意:實際開發(fā)中,理論上所有焦點問題都可以通過給dispatchKeyEvent方法增加監(jiān)聽來來攔截來控制。

(1)dispatchKeyEvent方法返回false后,先得到按鍵的方向direction值,這個值是一個int類型參數(shù)。這個direction值是后面來進(jìn)行焦點查找的。

(2)接著會調(diào)用DecorView的findFocus()方法一層一層往下查找已經(jīng)獲取焦點的子View。

ViewGroup的findFocus方法如下:

View的findFocus方法

說明:判斷view是否獲取焦點的isFocused()方法, (mPrivateFlags PFLAG_FOCUSED) != 0 和view 的isFocused()方法是一致的。

其中isFocused()方法的作用是判斷view是否已經(jīng)獲取焦點,如果viewGroup已經(jīng)獲取到了焦點,那么返回本身即可,否則通過mFocused的findFocus()方法來找焦點。mFocused其實就是ViewGroup中獲取焦點的子view,如果mView不是ViewGourp的話,findFocus其實就是判斷本身是否已經(jīng)獲取焦點,如果已經(jīng)獲取焦點了,返回本身。

(3)回到processKeyEvent方法中,如果findFocus方法返回的mFocused不為空,說明找到了當(dāng)前獲取焦點的view(mFocused),接著focusSearch會把direction(遙控器按鍵按下的方向)作為參數(shù),找到特定方向下一個將要獲取焦點的view,最后如果該view不為空,那么就讓該view獲取焦點。

(4)focusSearch方法的具體實現(xiàn)。

focusSearch方法的源碼如下:

可以看出focusSearch其實是一層一層地網(wǎng)上調(diào)用父View的focusSearch方法,直到當(dāng)前view是根布局(isRootNamespace()方法),通過注釋可以知道focusSearch最終會調(diào)用DecorView的focusSearch方法。而DecorView的focusSearch方法找到的焦點view是通過FocusFinder來找到的。

(5)FocusFinder是什么?

它其實是一個實現(xiàn) 根據(jù)給定的按鍵方向,通過當(dāng)前的獲取焦點的View,查找下一個獲取焦點的view這樣算法的類。焦點沒有被攔截的情況下,Android框架焦點的查找最終都是通過FocusFinder類來實現(xiàn)的。

(6)FocusFinder是如何通過findNextFocus方法尋找焦點的。

下面就來看看FocusFinder類是如何通過findNextFocus來找焦點的。一層一層往下看,后面會執(zhí)行findNextUserSpecifiedFocus()方法,這個方法會執(zhí)行focused(即當(dāng)前獲取焦點的View)的findUserSetNextFocus方法,如果該方法返回的View不為空,且isFocusable = true isInTouchMode() = true的話,F(xiàn)ocusFinder找到的焦點就是findNextUserSpecifiedFocus()返回的View。

(7)findNextFocus會優(yōu)先根據(jù)XML里設(shè)置的下一個將獲取焦點的View ID值來尋找將要獲取焦點的View。

看看View的findUserSetNextFocus方法內(nèi)部都干了些什么,OMG不就是通過我們xml布局里設(shè)置的nextFocusLeft,nextFocusRight的viewId來找焦點嗎,如果按下Left鍵,那么便會通過nextFocusLeft值里的View Id值去找下一個獲取焦點的View。

可以得出以下結(jié)論:

1. 如果一個View在XML布局中設(shè)置了focusable = true isInTouchMode = true,那么這個View會優(yōu)先獲取焦點。

2. 通過設(shè)置nextFocusLeft,nextFocusRight,nextFocusUp,nextFocusDown值可以控制View的下一個焦點。

Android焦點的原理實現(xiàn)就這些。總結(jié)一下:

為了方便同志們學(xué)習(xí),我這做了張導(dǎo)圖,方便大家理解~

Android移動應(yīng)用中的焦點分析

簡單一點理解,在移動應(yīng)用中,焦點就是當(dāng)前正在處理事件的位置。在手機(jī)應(yīng)用中,最有可能用到焦點的就是EditText,如果同一個界面中有多個EditText,通常情況下同一時間只有一個能夠輸入內(nèi)容,此時,這個EditText就獲取了焦點。

在Android中,對焦點的設(shè)置分為兩種情況,TouchMode和非TouchMode?,F(xiàn)在的手機(jī)基本都是觸摸屏,我們用手指觸摸屏幕來操作Android應(yīng)用時,處于TouchMode。除了TouchMode之外,還有非TouchMode,利用外接設(shè)備來操作應(yīng)用。比如鍵盤。使用Genymotion模擬器的時候,一個界面上有多個控件時,可以用電腦tab鍵來進(jìn)行移動,被選中的控件會高亮顯示,這時候就是非TouchMode,被選中的控件獲得了焦點。

在手機(jī)應(yīng)用中,用到焦點的時候并不多,但是TV應(yīng)用中,需要用遙控器來操作選中控件,這時候就需要對焦點進(jìn)行處理了。關(guān)于焦點,常用方法如下:

在View類中, isFocusable() 和 isFocusableInTouchMode() 獲取到的結(jié)果都是false,也就是說,直接繼承自View的控件是不能獲取焦點的。我們常用控件中對這兩個方法進(jìn)行了改寫,比如EditText,這兩個方法都是true,而Button則只有 isFocusable() 返回true。這也就是為什么我們用tab鍵選取Button的時候能夠高亮顯示,而鼠標(biāo)點擊(模擬觸控)的時候不能高亮顯示的原因了。如果想在點擊的時候也能高亮顯示Button,需要手動設(shè)置 setFocusableInTouchMode(true) ,就可以了。

如果想對控件的焦點狀態(tài)進(jìn)行監(jiān)聽,需要設(shè)置 setOnFocusChangeListener() ,只要控件的焦點狀態(tài)發(fā)生變化(獲得或者失去焦點),都會調(diào)用 onFocusChange 方法

關(guān)于焦點的移動,默認(rèn)的算法會尋找指定方向上最近的可以獲取焦點的元素(非TouchMode)。另外在創(chuàng)建控件的時候,也可以指定尋找焦點的方向,設(shè)置nextFocusDown、nextFocusLeft、nextFocusRight 和 nextFocusUp的值為指定元素就可以了??匆韵吕樱?/p>

這里指定了上面的button向上尋找焦點時,下一個元素是id為bottom的元素,也就是說,上面的Button在獲取了焦點之后,繼續(xù)按向上鍵,系統(tǒng)會將焦點移動到id為bottom的元素上,而不是繼續(xù)向上。

在開發(fā)手機(jī)應(yīng)用的過程中,對焦點的處理并不多,它與事件是兩個不同的體系,通常情況下焦點和事件是相互獨立并不沖突。但是在Button的點擊事件中會有一點問題。如果我們隊一個button設(shè)置了 setFocusableInTouchMode(true) ,使他可以獲取焦點,那么我們點擊這個button的時候,第一次點擊并不會執(zhí)行 onClick() 方法,而是執(zhí)行 onFocusChange() 。第二次點擊的時候才會執(zhí)行 onClick() 方法??雌饋砗孟?onFocusChange() 消耗了點擊事件,實際上并不是的。

這個問題我們看一下源碼就清楚了:

onClick() 方法是在onTouchEvent的ACTION_UP里調(diào)用的,看一下View的onTouchEvent方法:

可以看到,只有當(dāng)focusTaken為false的時候才會執(zhí)行onClick,focusTaken的值默認(rèn)是false的,但是在 isFocusable() isFocusableInTouchMode() !isFocused() 為true的時候,會去 requestFocus 獲取焦點,并將值賦給focusTaken。

關(guān)鍵在于 isFocused() ,如果當(dāng)前Button沒有獲取焦點, isFocused() 返回false, !isFocused() 值為ture,Button就會去獲取焦點,從而導(dǎo)致 focusTaken 為true, onClick 方法就不會執(zhí)行了,只有Button已經(jīng)獲取了焦點的時候才會執(zhí)行onClick方法。

Android TV 按鍵焦點事件分發(fā)流程詳解

DecorView →PhoneWindow →Activity→ViewGroup→view

下面我們根據(jù)按鍵事件的分發(fā)流程,抽絲剝繭,逐一分析。

private int processKeyEvent(QueuedInputEvent q)

1、DecorView.java

2、Activity.java

3、ViewGroup.java

4、View.java

通過該方法,接收器receiver的onKeyDown、onKeyUp、onKeyLongPress、onKeyMultiple等方法將被回調(diào)。

在上述按鍵事件的入口中提到的ViewRootImpl中

如果mView.dispatchKeyEvent(event)返回true,則結(jié)束事件分發(fā);

如果返回false,則調(diào)用如下方法

繼續(xù)執(zhí)行后續(xù)的焦點導(dǎo)航流程。

焦點導(dǎo)航的總體流程就是:

1、View focused = mView.findFocus();//從視圖樹的頂層,即DecorView一層一層的遞歸查找當(dāng)前獲得焦點的view

2、View v = focused.focusSearch(direction);根據(jù)導(dǎo)航的方向查找下一個可獲取焦點的view

3、v.requestFocus(direction, mTempRect)請求獲取焦點

4、v.requestFocus(direction,mTempRect)內(nèi)部,調(diào)用mParent.requestChildFocus(this, focused)逐層遞歸向上級通知

ViewRootImpl.java

mView即DecorView,從DecorView開始,一層一層的向下遞歸查找當(dāng)前獲得焦點的view

找到了當(dāng)前獲得焦點的focused,調(diào)用該焦點view的focusSearch(direction)方法查找direction方向上下一個將要獲取焦點的view。

focused.focusSearch(direction)實際上會調(diào)用mParent.focusSearch(this, direction)方法,層層遞歸,直到調(diào)用到DecorView的focusSearch(this, direction)方法。

而DecorView繼承ViewGroup,實際上最終會調(diào)用到FocusFinder.getInstance().findNextFocus(this, focused, direction),this 就是DecorView對象。

最終會調(diào)用到DecorView父類ViewGroup中的FocusFinder.getInstance().findNextFocus(this, focused, direction);

ViewGroup.java

FocusFinder.java

搜索到下一個獲取焦點的view后,調(diào)用該view.requestFocus(direction, mTempRect)方法

注意:調(diào)用requestFocus(direction, mTempRect)需要區(qū)分調(diào)用者。

如果是ViewGroup,則會更加焦點獲取策略,實現(xiàn)父View和子View之間獲取焦點的優(yōu)先級。

如下是ViewGroup.java 和View.java 中requestFocus方法是實現(xiàn):

ViewGroup.java

View.java

View獲取到焦點后,會調(diào)用mParent.requestChildFocus(this, focused)逐層遞歸向上級通知

ViewGroup.java

分享文章:android的焦點事件,android 焦點
網(wǎng)站網(wǎng)址:http://www.ekvhdxd.cn/article34/dscsose.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、定制開發(fā)、服務(wù)器托管、網(wǎng)站營銷全網(wǎng)營銷推廣、手機(jī)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)