惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
Y
Y Combinator Blog
博客园_首页
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
Hugging Face - Blog
Hugging Face - Blog
宝玉的分享
宝玉的分享
B
Blog
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
WordPress大学
WordPress大学
L
LangChain Blog
爱范儿
爱范儿
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
C
Check Point Blog
博客园 - 聂微东
云风的 BLOG
云风的 BLOG
Microsoft Security Blog
Microsoft Security Blog
博客园 - 叶小钗
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Help Net Security

博客园 - 翻滚的咸鱼

Ops权限行为记录源码解析 TaskView方案框架原理 视频解码器问题 常用命令 扫光动效 引导记录 性能优化 View共享动效 常用脚本文件 Launcher 卡片框架多模块集成 Launcher 桌面源码笔记二 Launcher 桌面源码笔记一 氛围灯动态屏保取色方案二 氛围灯动态屏保取色方案一 自定义最近任务管理器 anr问题 TV RecyclerView 焦点处理笔记 多类型适配器 多屏下字体自动取色 阻尼拉伸方案 图片区域点击处理 RecyclerView设置默认焦点跟多页面焦点抢占 Fragment翻盖动效 当个View下,使用Drawable入场退场动画
TV RecyclerView 滑动后操作保持落焦在左侧第一个View
翻滚的咸鱼 · 2024-05-28 · via 博客园 - 翻滚的咸鱼

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

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