版權宣告:
本賬號釋出文章均來自公眾號,承香墨影(cxmyDev),版權歸承香墨影所有。
未經允許,不得轉載。
一、前言
Android 系統的智慧電視,最近兩年基本上越來越火了,而在現在手機 App 開發中,都是使用觸控的模式進行操作,但是在開發智慧電視 App 的時候,View 的焦點定址,就是我們繞不過去的坎。
那麼,本文就 View 如何控制 Focus,來分析它背後的實現邏輯。
二、View 的焦點控制
在 Android 中,是有一套自己的焦點查詢的演算法,簡單來說,就近原則,就是按方向就近查詢下一個符合條件的 View。
如果我們有對一個 View 焦點控制的需求,需要對這個 View 強制指定它上下左右之後的下一個獲取焦點的 View。可以通過 View 的屬性來控制,只需要在對應方向上設定我們需要焦點轉移的下一個 View 的 ID 即可。
- android:nextFocusDown:按下的時候,焦點定址的 ViewId。
- android:nextFocusUp:按上的時候,焦點定址的 ViewId。
- android:nextFocusLeft:按左的時候,焦點定址的 ViewId。
- android:nextFocusRight:按右的時候,焦點定址的 ViewId。
- android:nextFocusForward:向前的時候,焦點定址的 ViewId。
這些都是最基本的,不是本文的主題。那麼如果沒有設定這些屬性,而 Android 對 View 的就近原則的焦點查詢演算法,到底是如何實現的呢?
三、View 的焦點定址
Android 中,是如何做到對 View 焦點的控制呢?
我們就先從 View 的原始碼看起,看看它是如何找到“下一個”位置的 View的,在 View 中,查詢下一個應該獲取焦點的 View ,使用的方法是 focusSearch()。
它需要傳遞一個 direction 引數,這個 direction 就是指定獲取什麼方向上的下一個位置的焦點。
而 View 並不會處理焦點定址的具體邏輯,而是將焦點的查詢委託給 mParent 來實現,mParent 是一個介面,它的實現類是 ViewRootImpl。
ViewRootImlp.focusSearch() 最終又將焦點定址的任務,交託給 FocusFinder 來處理。
findNextFocus() 中可以看到,findNextUserSpecifiedFocus() 方法正是用於查詢我們對 View 設定不同方向的下一個焦點的 ViewId ,它的優先順序是最高的,如果沒有找到,才會進行 findNextFocus() 通過演算法來查詢對應的 View。
為了證實這個說法,我們先看看 findNextUserSpecifiedFocus() 的原始碼。
最終,又調回到 View.findUserSetNextFocus() 方法去尋找。
到這裡也證實了我們的猜測,確實是通過 direction 來進行 View 的焦點定址。
再回過頭來看看 findNextFocus() 方法,如果通過 findNextUserSpecifiedFocus() 方法沒有找到我們指定的 View,就會繼續向下執行。這裡宣告的一個區域性變數 focusables 就是用於存放符合演算法的所有 View 。
接下來再看看如何向 focusables 這個 List 中,新增符合定址要求的 View。在 findNextFocus() 中可以看到,它最終會呼叫 findNextFocusInAbsoluteDirection() 方法。
從 findNextFocusInAbsoluteDirection() 方法可以看出,就近原則就是在這裡實現的,通過 View 的座標點,計算出最符合要求的 View ,最終將找到的 View 返回過去。
到這裡,基本就追蹤到 View 對焦點定址的完整邏輯。最後補一個方法呼叫的流程圖。
四、總結
如果有對 View 焦點的控制,可以考慮通過設定 View 的屬性,還可以通過重寫 View.focusSearch() 方法,來定製 View 焦點的定址規則。不過一般而言,不推薦重寫 focusSearch() 方法,只使用屬性控制也能滿足我們的需求。