在 recyclerview 中,想要无论滑动到哪,每次按遥控器落焦,需要落焦在左侧第一个 item 上面,如果不能触屏还好,触屏会导致焦点丢失

根据系统的反馈,如果你滑动了列表,刚好列表的 item 卡在一半的位置,此时系统的落焦规则,不一定会到第一个

之前试过一个效果一般的方案,就是通过 findFirstVisibleItemPosition 等方法,去自动获取可见的第一个下标,勉强可以达到重新落焦的预期

但是显然无法精确,如果想要精准的获取第一个item,这种方式显然不行,只能另辟蹊径

居然要求是左上角第一个,那么可以使用坐标来获取 child view,从而得到左侧第一个 item

internal fun RecyclerView.findChildView(x: Float, y: Float): View? {
    layoutManager?.let {
        for (i in childCount - 1 downTo 0) {
            val child = getChildAt(i)
            child?.let { view ->
                if (x >= view.left && x <= view.right && y >= view.top && y <= view.bottom) {
                    return view
                }
            }
        }
    }
    return null
}

这样你会发现,无论怎么滑动,只要 item 位于左上角,就能精准查找到 item view,然后通过 getChildLayoutPosition(view) 方法获取到在 adapter 中的下标

但是有个问题,假设当前坐标中,没有 item 的情况,此时,可以使用第一种方案 findFirstVisibleItemPosition 来获取

最后通过当前的情况去滚动 recyclerview 到指定位置

/** 重新获取焦点时自动滚动定位 */
    fun scrollBy() {
        val view = findChildView(x + 100f, y + 100f)
        val position = if (view == null) getCurrentFirstIndex() else getChildLayoutPosition(view)
        Logger.d("scrollBy position $position view is null ${view == null}")
        val firstItem: Int = speedLayoutManager.findFirstVisibleItemPosition()
        val lastItem: Int = speedLayoutManager.findLastVisibleItemPosition()
        //区分情况
        if (position <= firstItem) {
            //当要置顶的项在当前显示的第一个项的前面时
            speedLayoutManager.smoothScrollToPosition("scrollBy1", this, position)
            Logger.d("scrollBy smoothScrollToPosition1")
        } else if (position <= lastItem) {
            //当要置顶的项已经在屏幕上显示时
            val top: Int = getChildAt(position - firstItem).top
            smoothScrollBy(0, top)
            Logger.d("scrollBy smoothScrollBy")
        } else {
            //当要置顶的项在当前显示的最后一项的后面时
            speedLayoutManager.smoothScrollToPosition("scrollBy2", this, position)
            Logger.d("scrollBy smoothScrollToPosition2")
        }
        AppListKeyDownUtils.setFocus("scrollBy", position)
    }

这样在失去焦点后重新获取焦点时,落焦的位置就在左上角第一个了