1. 程式人生 > >Android原始碼剖析:FocusFinder

Android原始碼剖析:FocusFinder

由於工作研究,需要優化一下焦點尋找邏輯,所以首先研究一下Android原生邏輯。

從原始檔,我們可以看到,FocusFInder類大概1000行左右的程式碼量,並不多。

我們可以發現FucusFInlde使用了單例模式,不過由於使用了ThreadLocal,所以每一個執行緒都會擁有一個副本。

有2個方法可以尋找焦點,一種通過view,另外一種通過view的rec。

我們可以看到上述2個方法都呼叫了另外一個findNextFocus方法。effectiveRoot是指包含focused的ViewGroup,假如存在比root更加靠近的,並且getTouchscreenBlocksFocus(),isKeyboardNavigationCluster(),focused.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)同時為true的Viewgroup,否則就是root。

100行呼叫了findNextUserSpecifiedFocus方法,195行方法將會根據預先給View設定的NextFocusId尋找usersetNextFocus.。198行開始的while迴圈中,有一個預防迴圈判斷,由於findUserSetNextFocus是排除自身的,所以只需要間隔對比cycleCheck和userSetNextFocus即可排除迴圈。

108行呼叫addFocusables方法。1203行可以看到有一個FOCUS_BLOCK_DESCENDANTS的判斷,由於直接return,所以將會攔截子View獲取焦點,無論它自身是否可以處理。1214行有一個FOCUS_BEFORT_DESCENDANTS的判斷,將會優先由自身處理,然後再分派給子View。1222行判斷是否visible。

1226行呼叫FocusFinder中的sort方法。先利用rec.top,tec.bottom做一次排序,如下圖方塊1,2,3,4。844-861行,將會把方塊根據rec.left,rec.right排序為1,2,4,3,前提是isRtl為false。1235行有一個FOCUS_AFTER_DESCENDANTS的判斷,就是所有的子View都沒有處理的話,將嘗試自己處理。

110行呼叫findNextFocus方法,首先當focused不是null,可以直接獲取focusedRect,並且轉換座標系到root。假如focused為null,直接根據direction取root的某一個點作為focusedRect。

268行呼叫findNextFocusInAbsoluteDirection方法,由於已經獲取到focused相對於root的座標。340-352行,首先把mBestCandidateRect根據direction初始化一個不可能的值。

367行呼叫isBetterCandidate方法,判斷是否是一個更加好的預選值。主要是做交叉性,距離的對比優選,最後返回closeset。

最後,通過分析原始碼可以得知,每一次尋找焦點都需要呼叫focusables.cleat(),並且根據direction做繁雜的對比運算。在特定的場景,就是介面基本不會發生變化,我們可以唯一確定focusables佇列,然後把每一個view計算得出的left,up,right,down方向的focusView儲存起來,那麼在下一次焦點尋找的時候就可以直接獲取,不需要重複計算。