Android 的 View 焦點定址

承香墨影發表於2017-04-20

版權宣告:

本賬號釋出文章均來自公眾號,承香墨影(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()。

Android 的 View 焦點定址
f-fsearch.png

它需要傳遞一個 direction 引數,這個 direction 就是指定獲取什麼方向上的下一個位置的焦點。

而 View 並不會處理焦點定址的具體邏輯,而是將焦點的查詢委託給 mParent 來實現,mParent 是一個介面,它的實現類是 ViewRootImpl。

Android 的 View 焦點定址
f-vsearch.png

ViewRootImlp.focusSearch() 最終又將焦點定址的任務,交託給 FocusFinder 來處理。

Android 的 View 焦點定址
f-nextSearch.png

findNextFocus() 中可以看到,findNextUserSpecifiedFocus() 方法正是用於查詢我們對 View 設定不同方向的下一個焦點的 ViewId ,它的優先順序是最高的,如果沒有找到,才會進行 findNextFocus() 通過演算法來查詢對應的 View。

為了證實這個說法,我們先看看 findNextUserSpecifiedFocus() 的原始碼。

Android 的 View 焦點定址
f-user.png

最終,又調回到 View.findUserSetNextFocus() 方法去尋找。

Android 的 View 焦點定址
f-viewuser.png

到這裡也證實了我們的猜測,確實是通過 direction 來進行 View 的焦點定址。

再回過頭來看看 findNextFocus() 方法,如果通過 findNextUserSpecifiedFocus() 方法沒有找到我們指定的 View,就會繼續向下執行。這裡宣告的一個區域性變數 focusables 就是用於存放符合演算法的所有 View 。

接下來再看看如何向 focusables 這個 List 中,新增符合定址要求的 View。在 findNextFocus() 中可以看到,它最終會呼叫 findNextFocusInAbsoluteDirection() 方法。

Android 的 View 焦點定址
f-findnext.png

從 findNextFocusInAbsoluteDirection() 方法可以看出,就近原則就是在這裡實現的,通過 View 的座標點,計算出最符合要求的 View ,最終將找到的 View 返回過去。

Android 的 View 焦點定址
f-abs.png

到這裡,基本就追蹤到 View 對焦點定址的完整邏輯。最後補一個方法呼叫的流程圖。

Android 的 View 焦點定址
f-tu.png

四、總結

如果有對 View 焦點的控制,可以考慮通過設定 View 的屬性,還可以通過重寫 View.focusSearch() 方法,來定製 View 焦點的定址規則。不過一般而言,不推薦重寫 focusSearch() 方法,只使用屬性控制也能滿足我們的需求。

Android 的 View 焦點定址
公眾號二維碼.jpg

相關文章