[bas-leanback-tab]:Android TV TabLayout 、ViewPager场景解决方案

1、前言

大家应该非常熟悉在手机App开发中使用TabLayoutViewPager组件,但是手机的操作模式与电视上通过遥控器操作的方式不同,能否直接在TV开发中使用上述组件?如果不能直接使用是因为什么问题?

我没有测在TV开发中直接使用TabLayoutViewPager的情况,但阅读其源码之后猜测可能存在如下问题:

  • TabLayout在手机中使用通过Tab的点击触发响应,而在TV中,谷歌建议应该使用TabFocus变化来触发响应
  • TabLayoutitem view应该不能获取焦点(每个Tab本质对应的View是一个TabViewTabLayout内部基于LinearLayout实现的)。
  • ViewPager默认会根据按键的点击进行翻页,而在谷歌的一些文档或者代码中建议ViewPagerTV上如果跟TabLayout联用(或者有其他导航操作行为)的场景下应该禁止ViewPager通过左右键翻页(大致是这个意思,后面我贴原文)。

大致有以上问题,也没什么严重性问题,经过简单修改基本就能解决,但直觉告诉我官方应该有对应的库支持,不然TabLayout只能用在Phone上也太鸡肋了。 果不其然,经过一番搜索找到了,JitPack提供了leanback-tab,但显然本文的重点不是这个,而是自定义的leanback-tab

2、leanback-tab

官方提供了该库,用于提供TV开发使用TabLayout+ViewPager的联动场景,代码不多,大致功能如下:

2.1 LeanbackViewPager

ViewPager的基础上增加了以下内容:

  • 设置启用/禁用TouchEvent(默认禁用)
  • 设置启用/禁用KeyEvent(默认禁用)

代码非常简单,可以查看源码自行了解;

2.2 LeanbackTabLayout

在基于TabLayout的基础上增下了以下内容

  • ViewPager联动的情况下,Tab通过Focus改变切换ViewPager
  • 焦点记忆:即TabLayout在垂直方向上获取焦点时,会优先让之前已选中的Tab获取焦点

2.2 不足

虽然官方提供了该库用于支持,但是也是从非常基础的层面让TabLayoutViewPager在TV开发中能够联动使用,但并不意味着能够很好的使用和丰富的功能,有一些交互场景还待优化。

存在以下内容可以进一步优化:

2.2.1 ViewPager内部寻找下一个焦点View时会转移到ViewPager之外的View

有些情况在ViewPager内部响应遥控器左右按键时,我们希望焦点只是在ViewPager内部转移。

image.png

2.2.2 TabLayout左右边界Tab响应横向遥控器按键方向的寻焦问题

tablayout边界按键响应问题.jpg

有些情况在TabLayout内部响应遥控器左右键时,我们希望焦点只在TabLayout内部转移。

2.2.3 TabLayoutViewPager联用时Tab自定义View的问题

这是一个众所周知的问题吧(在手机开发中也是一样):我们可以通过tabLayout.newTab().setCustomView()设置Tab的自定义效果,但是通过TabLayoutsetupWithViewPager方法与ViewPager联用时,TabLayout默认会移除当前的所有已添加的Tab,然后通过ViewPager提供的PagerAdapter#getPageTitle去创建Tab,无法自定义Tab效果,当然也可以不调用setupWithViewPager方法采用手动绑定的方式实现TabLayoutViewPager的联动,但是这样子也的自己处理tabViewfocus切换逻辑(LeanbackTabLayout是在setupWithViewPager方法关联的viewpager,在focus监听中触发viewpager翻页的)

如果在配上TabLayout的动态刷新,Tab样式的动态变化,处理起来还是挺打脑壳的。

所以这些问题基本导致TabLayoutViewPager不是很方便的联用在一起;

3、bas-leanback-tab

本库在结合bas-leanback的基础上做了更多的扩展以解决TV上常规需求。

3.1 LeanbackViewPager

3.1.1 设置启用/禁用TouchEvent(默认禁用)

对应xml属性:app:touchEnabled_lbt

fun setTouchEnabled(enableTouch: Boolean)
复制代码

3.1.2 设置启用/禁用KeyEvent(默认禁用)

对应xml属性:app:keyEventEnabled_lbt

fun setKeyEventsEnabled(enableKeyEvent: Boolean)
复制代码

3.1.3 设置边界焦点移出与否(默认不允许)

对应xml属性:app:focusOutEnabled_lbt

/**
 * 优化边界寻焦规则:触发遥控器左右按键时,是否允许[ViewPager]内的焦点转移到[ViewPager]之外
 */
fun setFocusOutEnabled(enableFocusOut: Boolean) {
    focusOutEnabled = enableFocusOut
}
复制代码

3.2 LeanbackTabLayout

LeanbackTabLayout是基于TabLayout进行扩展的,所以TabLayout本身支持的功能基本上都是支持的,比如设置fixed tab或者scroll tab、设置tab选中的indicatortab下面的横线)等。

3.2.1 TV操作支持

对应xml属性:app:isLeanbackMode_lbtTV上运行时,通过TabFocus触发Tab的选中事件(手机上是通过Tab的点击)

fun setLeanbackMode(isLeanback: Boolean)
复制代码

默认支持TV操作,因为即便开启TV操作支持,也不影响在手机上运行。

注:本库的焦点事件绑定较为简单明了。

3.2.2 边界寻焦规则优化

对应xml属性:app:focusOutEnabled_lbt

/**
 * 优化边界寻焦规则:触发遥控器左右按键时,是否允许[TabLayout]内的焦点转移到[TabLayout]之外
 */
fun setFocusOutEnabled(enableFocusOut: Boolean) {
    focusOutEnabled = enableFocusOut
}
复制代码

3.2.3 焦点记忆

对应xml属性:app:focusMemoryEnabled_lbt TabLayout获取焦点时,之前已选中的tab优先获取焦点

fun setFocusMemoryEnabled(enableFocusMemory: Boolean)
复制代码

3.2.4 ViewPager联动使用场景、自定义Tab处理等

支持系统的setupWithViewPager方法

fun setupWithViewPager(viewPager: ViewPager?, autoRefresh: Boolean)
复制代码

支持与ViewPager联用时自定义Tab样式,可以轻易做到文本、文本图标、自定义、甚至完全混合样式的Tab使用场景。

/**
 * 根据ViewPager进行初始化;
 * @param tabConfigurationStrategy tab配置策略,用于自定义Tab样式;
 * [TabConfigurationStrategy.TextStrategy]:文本tab策略
 * [TabConfigurationStrategy.ViewPagerStrategy]:(系统TabLayout与ViewPager联用时的规则相似) 使用ViewPager的adapter提供的getPageTitle方法创建文本Tab策略
 * [TabConfigurationStrategy.TextIconStrategy]:文本+图标策略
 * [TabConfigurationStrategy.CustomViewStrategy]:自定义view策略,[TabConfigurationStrategy.CustomViewFactory]
 * 如果以上策略不能满足需求,可以自定义实现[TabConfigurationStrategy]。
 * @param autoRefresh [ViewPager.getAdapter]数据改变时是否自动刷新Tab
 */
fun setupWithViewPager(
    viewPager: ViewPager?,
    tabConfigurationStrategy: TabConfigurationStrategy,
    autoRefresh: Boolean = true
) 
复制代码

3.2.5 Adapter与Tab的刷新

只要ViewPager更改了Adapter,或者调用了AdapternotifyDataSetChanged 方法通知刷新更新,TabLayout将同步更新。

3.2.6 其他说明

TabLayoutViewPager的联动实现已完全封装到LeanbackTabLayoutMediator类中,该类也可以单独使用。

3.3 使用

该库使用非常简单,查看LeanbackTabLayout的setupWithViewPager方法即可明白;另外支持的功能前面已列出对应的api和属性,随时调用设置即可。

//最简单的使用方式:Tab会根据Adapter的getPageTitle方法显示文本Tab效果
binding.tabLayout.setupWithViewPager(
    binding.tabViewPager
)

//自定义Tab配置策略使用方式:库内置了几种Strategy,ViewPagerStrategy对应系统的处理方式,也就是跟前面的使用方式是一样的,其他策略见TabConfigurationStrategy
binding.tabLayout.setupWithViewPager(
    binding.tabViewPager,TabConfigurationStrategy.ViewPagerStrategy(binding.tabViewPager)
)

复制代码

3.4粗略Demo截图

text_tab.jpg

text_icon_tab.jpg

custom_tab.jpg

mixed_tab.jpg

refresh.jpg

最后贴上本库的Github地址

猜你喜欢

转载自juejin.im/post/7019107092334116872