RecyclerView设置默认焦点跟多页面焦点抢占
场景:多个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() } } }