场景:多个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()
                }
            }
        }