让遥控器不再打滑:TV 直播应用的焦点与布局 4 个关键决策
2026/9/2 9:56:46 网站建设 项目流程

让遥控器不再打滑:TV 直播应用的焦点与布局 4 个关键决策

【免费下载链接】my-tv我的电视 电视直播软件,安装即可使用项目地址: https://gitcode.com/GitHub_Trending/my/my-tv

遥控器按了半天,焦点还在原地打转,换台却把频道列表翻到了底?TV 直播应用 my-tv 用「全屏播放 + 悬浮层」的布局结构和启动即定焦点的写法,把这个问题处理得很克制。照着它的思路改你的项目,换台、看节目单、进设置都能压到 3 次按键以内,焦点不再「失踪」。

  • 掌握 3 个让屏幕在 3 米外可读的布局决策:单容器骨架、40sp 大号频道号、悬浮层定位
  • 排查「焦点卡死」问题只需 2 步:确认启动时的焦点起点,再检查按键事件的分发表
  • 复盘 4 次真实迭代(v1.7.2 → v2.0.0),看懂频道列表怎么从手机移植版变成 TV 原生布局
  • 记住 2 个避坑点:别把手机的列表布局直接搬上电视,Fragment 切换后不调requestFocus方向键会「失聪」

定骨架:TV 直播应用主界面为什么只要 1 个 FrameLayout

这一章解决的是「主界面用什么容器」的问题——答案可能简单到让你意外。

my-tv 的主界面文件 app/src/main/res/layout/activity_main.xml 全文只有一个 FrameLayout,它的作用就是一个占满全屏的容器,频道号、节目信息、设置菜单这些 UI 都以悬浮层的形式盖在视频之上:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_browse_fragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" tools:deviceIds="tv" />

注意最后的tools:deviceIds="tv"——它声明这个布局是面向 TV 设备设计的,开发阶段就能按大屏预览。

这个结构背后的思路:电视上的主场景是「视频铺满全屏」,所有交互元素都是临时浮上去的。这和手机 App「内容列表为主、全屏只是其中一页」的组织方式正好相反。

⚠️避坑:如果你的项目是从手机 App 移植过来的,先检查根布局是不是 RecyclerView 套 NestedScrollView 那套——用户在 3 米外不会逐行浏览你的列表,他们要的是画面 + 少量悬浮入口。

骨架定了,下一个问题立刻出现:全屏容器里焦点一开始该在哪?

找回焦点起点:启动和换页时把 requestFocus 写对

焦点「打滑」的常见根因,是界面上没人对焦点负责。

my-tv 在 app/src/main/java/com/lizongying/mytv/MainActivity.kt(应用入口,负责按键分发和 Fragment 管理)里,主 Fragment 加载完成后直接显式要焦点:

// MainActivity.kt 启动阶段:把焦点起点钉在主 Fragment 上 mainFragment.view?.requestFocus()

你可以把requestFocus()理解成给遥控器指了个「起点」:用户拿起遥控器按第一下方向键时,系统才知道该往哪个方向找下一个目标。不做这一步,焦点可能落在一个不可聚焦的容器里,之后按任何方向键都无反应——这就是用户嘴里「遥控器坏了」的真相。

📌实操技巧:TV 项目里每次 Fragment 切换、每次重建视图,都值得问一遍「焦点现在在哪」。这条排查路径能帮你 2 分钟内定位绝大多数「方向键失灵」工单。

焦点有了起点,接下来要让每个方向都「有路可走」——这就轮到按键路由出场了。

给遥控器每个键分配固定职责:按键事件这样分发

TV 遥控器没有鼠标、没有右击,全部操作都压在一把键帽上,所以每个键的含义必须稳定、可预期。

my-tv 的做法是在 MainActivity.kt 里用一张when分发表,把不同来源的按键归并成少数几个动作。下面是这张分发表的骨架(各分支具体动作省略):

when (keyCode) { KeyEvent.KEYCODE_BACK -> { /* 退出 / 返回 */ } KeyEvent.KEYCODE_MENU -> { /* 呼出菜单 */ } KeyEvent.KEYCODE_DPAD_UP -> { /* 换台 */ } KeyEvent.KEYCODE_CHANNEL_UP -> { /* 换台 */ } KeyEvent.KEYCODE_DPAD_DOWN -> { /* 换台 */ } KeyEvent.KEYCODE_CHANNEL_DOWN -> { /* 换台 */ } KeyEvent.KEYCODE_DPAD_LEFT -> { /* 打开频道列表 */ } KeyEvent.KEYCODE_DPAD_RIGHT -> { /* 打开设置菜单 */ } }

这段在干什么?把「方向键上滑/下滑」和「专用换台键 CHANNEL_UP/DOWN」归并到同一个换台动作,保证不同品牌遥控器(有的只有方向键,有的带频道键)按出来的效果完全一致。

上下的语义可以对照 my-tv 仓库里这张按键示意:上/下负责换频道,中间确认键用于呼出菜单,左右键分别打开频道列表和设置。

⚠️避坑:改键位语义要慎重。my-tv 在 v1.7.8 把「左键开频道列表、右键开设置」这套语义定下来后就没再动过——键位是用户用肌肉记忆学会的,改一次就得教育一遍。

按键路由解决了「去哪」,最后一个视觉问题是:到了的地方,3 米外看得清吗?

按 3 米外能看清的标准定字号与间距

TV 界面的字号标准不是「手机上舒服」,而是「沙发上不眯眼」。

频道号悬浮层 app/src/main/res/layout/channel.xml(右上角的换台提示)就给出了项目里最直白的一组参数:

<TextView android:id="@+id/channel_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:textColor="#FFEEEEEE" android:textStyle="bold" android:textSize="40sp" />

40sp 加粗、浅灰白文字,配合深色播放背景,就是「远距离可见性」的全部秘密:字号给够、字重加粗、对比度拉满。

间距上项目遵循 8dp 的倍数网格:channel.xml 里悬浮层距顶部 20dp、距右缘 170dp,都是刻意留出的「安全区」,让频道号既不挡画面主体,又不会贴边。颜色方面,项目把各状态色值集中放在 app/src/main/res/values/colors.xml(全局颜色值定义),方便统一调整焦点高亮与正文的对比度。

📌实操技巧:写完布局后别只看手机模拟器,用 TV 预览(或把模拟器分辨率调成 1080p 放到 1 米外看)验收一遍,字号不够的问题一眼就能暴露。

视觉和按键都就位了,但真实项目很少一步到位——频道列表这块就反复返工了 4 个版本。

频道列表的 4 次迭代:从手机移植版到 TV 原生布局

频道列表是 my-tv 里交互密度最高的组件,它的演进过程完整地记录在 HISTORY.md(项目更新日志),值得当成案例拆解。

最初的方案是把手机上的列表样式直接搬上来。很快暴露出两类问题:一是样式切换后需要重启才生效,二是列表展开后「按哪关不掉」说不清。

于是有了这几轮修改(均以更新日志原文为准):

  • v1.7.2:频道列表支持网格和行两种样式切换——承认了「一屏放 8 个台」和「一屏看 3 个台」是两种需求
  • v1.7.8:左键开列表、右键开设置,同时定了「点空白处收起列表」,把「怎么关掉」写进了规则
  • v2.0.0:修复网格样式下点空白处不退出、样式切换不生效两个回归问题——双样式带来的边界情况终于补齐

对照 app/src/main/res/layout/(全部页面布局文件)可以看到,最终形态就是「一个全屏容器 + 几个定位明确的悬浮层」:列表、节目信息、时间、设置各自独立成页,互不嵌套。

🎯复盘启示:这四版迭代里没有任何「大重构」,全部是对交互细节的修补——样式开关要即时生效、每个入口要有明确的出口。TV 项目的大部分返工都发生在这类「小事」上。

收束:三条能带走的判断标准

  • 钉住焦点起点:启动和每次页面切换后,确认requestFocus()已经执行
  • 给每个键固定一个职责:换台键、方向键、左右键各管一件事,并且长期不改
  • 按 3 米验收字号和间距:40sp 频道号、8dp 间距网格、深浅对比拉满

想深挖的话,这三个位置值得先看:

  • app/src/main/java/com/lizongying/mytv/MainActivity.kt:按键分发和焦点起点的核心实现
  • app/src/main/res/layout/channel.xml:远距离可见性的一组参考参数
  • HISTORY.md:频道列表 4 次迭代的完整时间线

如果这篇帮到了你,可以去 star 一下项目,或者git clone https://gitcode.com/GitHub_Trending/my/my-tv把代码拉下来跑起来看。下期我们聊一个更硬核的话题:TV 应用里播放失败怎么重试才能不黑屏——更新日志里「修复不能播放」出现过十几次,背后的重试逻辑值得单独写一篇。

【免费下载链接】my-tv我的电视 电视直播软件,安装即可使用项目地址: https://gitcode.com/GitHub_Trending/my/my-tv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询