在 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) }
這樣在失去焦點後重新獲取焦點時,落焦的位置就在左上角第一個了