TV RecyclerView 滑動後操作保持落焦在左側第一個View

翻滚的咸鱼發表於2024-05-28

在 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)
    }

這樣在失去焦點後重新獲取焦點時,落焦的位置就在左上角第一個了

相關文章