RecyclerView設定預設焦點跟多頁面焦點搶佔

翻滚的咸鱼發表於2024-06-18

場景:多個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()
                }
            }
        }

相關文章