聊一聊 Leanback 中的 HorizontalGridView

云视听极光截图.png

  Google 的 Leanback 有其固定的风格,但国内的UI往往不会按照 Leanback 的风格进行设计,比如说上面的云视听极光。这就导致 Android TV 的那些封装好的 Fragment、Presenter 等控件我们基本上用不到,局限性很强,所以,如果想要既使用 Leanback 的放大效果,又要同时按国内的 UI 风格做出国内界面的样子的话,只能使用适应性较强的控件,比如如 HorizontalGridView、VerticalGridView。那就要求我们要尽可能的熟悉 HorizontalGridView、VerticalGridView 这些控件的使用方式。
  下面就记录一下 HorizontalGridView 相关的东西。
  HorizontalGridView 继承自抽象类 BaseGridView,而 BaseGridView 又继承自 RecyclerView,也就是说 HorizontalGridView 是 RecyclerView 的子类,对于 HorizontalGridView 来说,RecyclerView 有的特性基本上也适用于 HorizontalGridView,也可以认为 HorizontalGridView 就是针对 TV 再次封装的 RecyclerView 。
  这次就对照下图所示的云视听极光的标题栏来写个 demo ,顺便梳理一下 HorizontalGridView 的一些属性和方法。

image.png

1 基本使用

1.1 新建项目,添加依赖

  如果想要使用Leanback,首先要添加 如下所示的 Leanback 的依赖。

    implementation 'com.android.support:leanback-v17:28.0.0'

1.2 修改 activity_main.xml

  在 activity_main.xml 中添加标题栏的 HorizontalGridView。修改后如下所示。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:background="#000000">

    <android.support.v17.leanback.widget.HorizontalGridView
        android:id="@+id/hg_title"
        android:layout_width="wrap_content"
        android:layout_height="50dp" />

</android.support.constraint.ConstraintLayout>

1.3 创建 Presenter

  Presenter 是 HorizontalGridView 用来绑定、展示子 Item 的类,类比着想的话,它就是和 RecyclerView 的 Adapter 起着一样的作用的东西,它们都有 onCreateViewHolder、onBindViewHolder 这样的方法,都有继承自 ViewHolder 的静态内部类。看看下面 TitlePresenter 这个类就会觉得十分熟悉。
  那就分析分析吧。
  一样之处:它们都是在 onCreateViewHolder 里加载 item 的布局,在 onBindViewHolder 里绑定数据,在内部类 ViewHolder 中接收由布局生成的子 View,并加载子 View 中的控件。
  不一样之处:不一样的是 RecyclerView 的 Adapter 有 notifyDataSetChanged、notifyItemChanged、notifyItemMoved、notifyItemRangeChanged 这样的数据刷新的方法,而在 HorizontalGridView 中,这些功能都抽出来了,它们都放到了 ObjectAdapter 这个抽象类及其子类中了,这是 MVP 设计模式的写法,同时我觉得这也体现了单一职责的思想。
  这里还有一些不关键的类和 xml 没贴出来,有需要去文章末尾百度云下载项目看吧。

TitlePresenter.class

package isuperred.github.com.horizontalgeidviewdemo;

import android.support.v17.leanback.widget.Presenter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class TitlePresenter extends Presenter {
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_title, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object o) {
        if (o instanceof Title) {
            ViewHolder vh = (ViewHolder) viewHolder;
            vh.tvTitle.setText(((Title) o).getName());
        }
    }

    @Override
    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {

    }

    static class ViewHolder extends Presenter.ViewHolder {
        TextView tvTitle;

        public ViewHolder(View view) {
            super(view);
            tvTitle = view.findViewById(R.id.tv_title);
        }
    }
}

1.4 初始化 HorizontalGridView 并为之绑定适配器、数据

  先通过 findViewById 获取 HorizontalGridView 的对象,通过 setHorizontalSpacing 这个方法在 item 之间添加间距,ArrayObjectAdapter 用于数据的绑定、数据的刷新,ItemBridgeAdapter 也可以进行数据刷新,它是全部刷新一遍,比较重,它更重要的是起一个桥梁的作用,将 ObjectAdapter、PresenterSelector、Presenter联系起来。
MainActivity.class

package isuperred.github.com.horizontalgeidviewdemo;

import android.os.Bundle;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.FocusHighlight;
import android.support.v17.leanback.widget.FocusHighlightHelper;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.support.v17.leanback.widget.ItemBridgeAdapter;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        HorizontalGridView horizontalGridView = findViewById(R.id.hg_title);
        horizontalGridView.setHorizontalSpacing(30);
        ArrayObjectAdapter arrayObjectAdapter = new ArrayObjectAdapter(new TitlePresenter());
        ItemBridgeAdapter itemBridgeAdapter = new ItemBridgeAdapter(arrayObjectAdapter);
        /*FocusHighlightHelper.setupBrowseItemFocusHighlight(itemBridgeAdapter,
                FocusHighlight.ZOOM_FACTOR_LARGE, true);*/
        horizontalGridView.setAdapter(itemBridgeAdapter);
        arrayObjectAdapter.addAll(0, TitleModel.getTitleList());

    }
}

1.5 效果图

  代码写完效果就是下面这样了,我这是在电视上运行后的效果。

效果图.png

2 一些属性、方法

2.1 focusOutFront、focusOutEnd

image.png

  像上面云视听极光所示,如果标题栏使用 HorizontalGridView 实现,内容区域使用 Fragment 里放的 VerticalGridView 实现,可能出现标题栏和内容区焦点切换不成功的问题,比如说,焦点不能从内容区切到标题栏这样的情况。这时使用 focusOutFront 和 focusOutEnd 属性能够解决问题,解决不同容器里焦点切换不成功的问题。使用方式如下所示。

<android.support.v17.leanback.widget.HorizontalGridView
        android:id="@+id/hg_title"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        app:focusOutEnd="true"
        app:focusOutFront="true"/>

  这2个属性在源码中是通过调用 GridLayoutManager 的 setFocusOutAllowed 方法来使用的。

image.png

2.2 setHorizontalSpacing

  没什么说的,就是设置 HorizontalGridView 的 Item 之间的间距。setHorizontalMargin 已经被弃用了。

image.png

2.3 setFocusScrollStrategy

  setFocusScrollStrategy 用来设置焦点的滚动方式,它的参数有3个可选值,分别为 FOCUS_SCROLL_ALIGNEDFOCUS_SCROLL_ITEMFOCUS_SCROLL_PAGE,默认值为 FOCUS_SCROLL_ALIGNED;值得一提的是,HorizontalGridView 和 VerticalGridView 的默认焦点搜索规则是溯源原则,而设置焦点滚动方式为 FOCUS_SCROLL_ITEM 后,焦点搜索规则变为了就近原则。
setFocusScrollStrategy 使用方式如下所示。

     horizontalGridView.setFocusScrollStrategy(HorizontalGridView.FOCUS_SCROLL_ITEM);
     verticalGridView.setFocusScrollStrategy(HorizontalGridView.FOCUS_SCROLL_ITEM);

  FOCUS_SCROLL_ALIGNED:焦点在中间

焦点在中间.gif

  FOCUS_SCROLL_ITEM:焦点在末尾

焦点在末尾.gif

  FOCUS_SCROLL_PAGE:翻页

翻页.gif

2.4 setNumRows

  setNumRows 用于设置行数,默认 HorizontalGridView 为一行,通过 setNumRows 方法可以设置多行。但有个注意点,设置多行后要注意 position 的位置。举个例子,2 行的 HorizontalGridView,第一行第一个模块 position 为 0,而 position 为 1 的模块是 第二行第一个,而非第一行第二个。如下图所示。

image.png

2.5 setRowHeight

  注意了,setRowHeight 是用来设置 HorizontalGridView 的 Item 的高度,而不是用来设置 HorizontalGridView 的高度。

2.6 setSelectedPosition、setSelectedPositionSmooth

  setSelectedPosition 和 setSelectedPositionSmooth 都是让某个 position 获取焦点,区别在于 setSelectedPositionSmooth 在移动时更平滑一点。

2.7 duplicateParentState

image.png

  这个属性还是很有用的。举个例子,上图是小米电视的【我的应用】页面,体验一下能够感受到:它的放大的焦点 View 是每个 Item 的最外层布局,而不是图标那个View,但是其焦点框却套在了图标那个 View 上,那这种效果如果我来实现就会用到 duplicateParentState 属性了。duplicateParentState 的意思是:当前控件是否跟随父控件的(点击、焦点等)状态。下面简单写一下其使用方式。

image.png

  如上图所示,最外层布局设置可点击、可获取焦点,在 ImageView 里设置了 duplicateParentState 属性,表示跟随父控件状态,所以在父控件获取焦点时,ImageView 也可以获取焦点。ImageView 设置了一个背景 bg_focus_border,bg_focus_border 是一个Selector,里边设置了焦点态和非焦点态的边框。这样,Item 的获取焦点时,图标就会显示有焦点框了。

3 代码

  没多少代码,就只传了标题栏的代码。
  百度云盘下载代码链接:https://pan.baidu.com/s/14Rf1XZMSxJQCH6dlmd4ICw
  提取码:gkaj

4 结语

  还有很多不会的,不知道的东西,慢慢学习吧。路漫漫其修远兮!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容