在 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) }
这样在失去焦点后重新获取焦点时,落焦的位置就在左上角第一个了