場景:多個tab切換,顯示不同的Fragment,其中一個Fragment佈局是兩個RecyclerView,分別位於左右兩側 需求:首次從tabView切換到改tab頁時,焦點從tabView首次往下移動時,需要落焦在右側的第一個item上面 如果按照系統原生邏輯,從tabView下移,可能預設位 ...
場景:多個tab切換,顯示不同的Fragment,其中一個Fragment佈局是兩個RecyclerView,分別位於左右兩側
需求:首次從tabView切換到改tab頁時,焦點從tabView首次往下移動時,需要落焦在右側的第一個item上面
如果按照系統原生邏輯,從tabView下移,可能預設位置不會在右側,此時需要確保,每次往下移動,焦點都需要在右側第一個
方案1、初始化時就 requestFocus 肯定行不通,因為切換tab時就會觸發
方案2、使用自定義View,重寫 addFocusables ,把預設焦點添加進列表,此時系統預設尋找的焦點就是設置的view
方案可行,前提是沒有焦點搶占問題(比如多tab同時存在時會有焦點搶占問題)
比如有個切換動效,動效存在時切換的兩個頁面會同時存在並且顯示在前臺,這個時候,快速切換tab並且往下移動,會發現動畫未結束,落焦會跑到上一個tabView的頁面上,然後動畫結束,上一個頁面隱藏,焦點在回到當前唯一顯示的頁面,雖然最後焦點回到了當前頁面,可是會有一個焦點延遲閃爍的過程;
又或者你在其它地方主動 request 了,導致焦點衝突閃爍問題;
方案3、強行 requestFocus ,行為粗暴,並且有的情況下會多次請求,導致焦點閃爍問題
終極方案、使用父容器控制子容器焦點,哪怕焦點被搶占,依然可以絲滑落焦
不要一下子請求子View的焦點,把焦點定在最外層的父容器上面,系統預設移動是尋找最近的view作為落焦點,父容器無疑是最近的
監聽父容器的焦點變化,如果存在焦點,在請求子View焦點(父容器上落焦狀態需要隱藏,達到無痕狀態)
這個時候你會發現,無論焦點是被其它view搶占,還是其它視窗搶占,都能回到你指定的view上
mBinding.rootView.setOnFocusChangeListener { v, hasFocus -> val focusItemView = mBinding.rvMode.getChildAt(0) Logger.d("FocusChange hasFocus $hasFocus itemFocus ${focusItemView.hasFocus()}") if (hasFocus && !focusItemView.hasFocus()) { mBinding.rvMode.post { focusItemView?.requestFocus() } } }