不断学习,做更好的自己!💪

前言

在 TV 端开发中,焦点处理是一个非常重要的技术。

一、父容器与子控件焦点获取关系处理

在布局文件中 , 父容器的节点中使用 ​​android:descendantFocusability​​ 属性 , 用于设置父容器与子组件之间的 焦点获取先后顺序 。

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:descendantFocusability="afterDescendants">

</LinearLayout>

android:descendantFocusability 属性取值 :

  • beforeDescendants : 父容器优先获取焦点 , 如果父容器不需要焦点 , 子组件才能获取到焦点 ;
  • afterDescendants : 子组件 优先获取焦点 , 如果子组件不需要获取焦点 , 则父容器获取焦点 ;
  • blocksDescendants : 只有 父容器 能获取焦点 , 子组件不能获取焦点 。

二、不同 TV 设备上的兼容问题

在开发时遇到这样一种情况 , 布局的样式是 ​​ScrollView​​​ 中嵌入一个 ​​ConstraintLayout​​​ 布局 , 在 ​​ConstraintLayout​​ 布局中设置了很多需要获取焦点的子组件 ;

  • 运行正常的情况 : 在 ​​Google​​ 提供的模拟器上运行时 , 正常运行 , ​​ScrollView​​ 的子组件中可以正常获取焦点 ;
  • 运行失败的情况 : 但是在真实的国产电视盒子中 , ​​ScrollView​​ 始终组织其子组件获取焦点 , 即使设置了 ​​android:descendantFocusability=“afterDescendants”​​ , 子组件也无法获取焦点 ;
  • 最终的解决方案 : 在子组件中 , 将需要获取焦点的组件都添加 ​​android:focusable=“true”​​ 属性 , 这样就解决了上述问题 ;

由此可见 , 相同的代码 , 在不同型号 、版本、厂家的电视设备上 , 焦点的获取 , 移动 , 表现是不一样的 , 因此这里就涉及到了焦点的兼容问题 ;

建议 : 为了适配尽可能多的电视设备 , 推荐如下做法 :

  • 设置可获取焦点 : 给需要获取焦点的组件 , 统一添加 ​​android:focusable="true"​​ 属性 ;
  • 设置不可获取焦点 : 凡是不需要获取焦点的组件 , 统一添加 ​​android:focusable="false"​​ 属性 ;
  • 设置组件兼容 : 凡是涉及到父容器与子组件之间的焦点获取的情况 , 统一使用​​android:descendantFocusability​​ 属性 。

三、按键获取焦点

按键获取焦点 : 在手机上按键获取焦点已经不常用 , 使用遥控器 / 手柄 控制界面需要关注该操作 ;

在 xml 布局文件中 , 在组件节点上设置如下属性 , 取值 true 或 false ;

android:focusable="true"

按键获取焦点一般是手机自带的物理键盘 , ​​D-Pad​​ 遥控器 ( 电视遥控器 ) , 游戏手柄 等 , 使用方向键 , 控制焦点改变 ;

如果该 ​​android:focusable​​​ 属性设置为 ​​true​​ , 则说明该组件可以获取焦点 , 按照不同的方向按键 , 焦点跳转到本组件设定的对应方向上的件 id 对应的组件 ;

四、触摸获取焦点

触摸获取焦点 : 目前的触摸屏手机控制焦点的主流操作 ;

在 xml 布局文件中 , 在组件节点上设置如下属性 , 取值 true 或 false ;

android:focusableInTouchMode="true"

​Button​​​ , ​​TextView​​​ , 布局组件 , 等默认没有触摸焦点 , 因为这些组件可能用于点击事件 , 如果这些组件可获取焦点 , 用户点击这些组件后 , 要先获取焦点 , 触发 ​​OnFocusChangeListener​​ 回调 , 获取焦点后才能进行点击 ;

​EditText​​ 默认自动获取焦点 , 并且进入界面抢先获取焦点 , 该组件需要有光标 , 并且弹出软键盘 ;