BrowseFragmentの構成 – Android TVアプリ開発入門2

BrowseFragmentの構成

本章では、Leanbackライブラリのメインとして使われるBrowseFragmentについて説明する。BrowseFragmentに、HeaderとListの組み合わせを格納していくことによって、コンテンツをリスト上に表示することができる。ちなみに、BrowseFragmentのソースコードは sdk上  (android/support/v17/leanback/app/) で見ることができる。 

下の写真は Android TV のサンプルアプリのメインページで、これがBrowseFragmentによって作成されている。 コンテンツがグリッド上に並べられ、ヘッダーごとにカテゴリ分けされているのがわかる。それぞれのヘッダーに対応してコンテンツの列があり、1対1対応している。この “ヘッダー + コンテント列” の組み合わせは ListRow で作られる。 BrowseFragment の中身(本体)は  ListRow のセットだということだ。今後このセットのことを RowsAdapter と呼ぶ。

下の写真に対応関係を図示した。 ListRow は青丸で囲ってある部分、RowsAdapter ListRowすべてを囲っている青い四角の部分に相当する。 

RowsAdapter1
ListRow の集合で RowsAdapter が構成される。これが BrowseFragment のメインコンテンツとなる。

ListRow 部分に注目してみよう。各コンテンツ (以降 CardInfo や アイテム と表記)は ArrayObjectAdapter の中に格納される(ここでは RowAdapter と表記)。   

このアイテムはどんなクラス、オブジェクトでもよくそのUI表示は Presenter クラスによって指定される。 

ListRow1
ListRowの構成

まとめ

 ArrayObjectAdapter (RowsAdapter) ← ListRow の集合
 ListRow = HeaderItem + ArrayObjectAdapter (RowAdapter)
 ArrayObjectAdapter (RowAdapter) ← アイテムの集合

Presenter クラス

アイテム(カード)のデザインは Presenter が担う。 Presenter はその名の通り、どのようにアイテムを表示するかを決めるクラスだ。 Presenter クラス自体は抽象クラスで、このPresenterクラスを継承したサブクラスに、アプリに適した具体的なUI表示の実装を行う。

Presenter クラスを継承するときには、以下の3つのメソッドをオーバーライド(実装)が必須となる。 

  • onCreateViewHolder(Viewgroup parent)
  • onBindViewHolder(ViewHolder viewHolder, Object cardInfo/item)
  • onUnbindViewHolder(ViewHolder viewHolder)

それぞれのメソッドの詳細は Presenter クラスのソースコードに記載されているので参照してほしい。 Presenter クラスは ViewHolder をインナークラスとして持っていて、これが View の参照を保持する。 アイテムオブジェクトに応じて、 View のUIを実装したい場合は viewHolder を介してViewにアクセスすることができる。アクセスするタイミングは onBindonUnbind といったそれぞれのイベントにリスナーが用意されていて、その中でUI処理を行う(後述)。

実はこれはModel-View-Presenter (MVP)アーキテクチャによるもの。コンテンツをModel Objectで扱う際、そのUI表示のテンプレートデザインをViewが担当し、ModelとViewのつなぎ役をPresenterが担当する。より細かくMVPに関して知りたい方は14章を参照。

HeadersFragment & RowsFragment (GridItemPresenter) の実装

説明はここまでにして、ここから実装。ここではPresenterの1例として GridItemPresenter クラスを実装する。
ここでは”アイテム”(Model)は  String クラス、 viewHolder は TextView (View)を保持する。

Viewに関する設定を、Presenterが行う。初期化処理は onCreateViewHolder()にて。 
実際にアイテムをViewと結びつける処理は onBindViewHolder() にて行われる。onBindViewHolderでは、第1引数で viewHolder を受け取ることができ、これが onCreateViewHolder 時に作成したViewを保持している。また第2引数で実際に表示するアイテムのインスタンスを受け取っている。

    private class GridItemPresenter extends Presenter {
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent) {
            TextView view = new TextView(parent.getContext());
            view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT));
            view.setFocusable(true);
            view.setFocusableInTouchMode(true);
            view.setBackgroundColor(getResources().getColor(R.color.default_background));
            view.setTextColor(Color.WHITE);
            view.setGravity(Gravity.CENTER);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
            ((TextView) viewHolder.view).setText((String) item);
        }

        @Override
        public void onUnbindViewHolder(ViewHolder viewHolder) {

        }
    }

}

実際にPresenterクラスの実装ができたら、これを RowsAdapter にセットすることで適用できる。これはActivityの作成時に行う。 以下では MainFragment の onActivityCreated() に実装

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        Log.i(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);

        setupUIElements();

        loadRows();
    }

    ...

    private void loadRows() {
        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());

        /* GridItemPresenter */
        HeaderItem gridItemPresenterHeader = new HeaderItem(0, "GridItemPresenter");

        GridItemPresenter mGridPresenter = new GridItemPresenter();
        ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
        gridRowAdapter.add("ITEM 1");
        gridRowAdapter.add("ITEM 2");
        gridRowAdapter.add("ITEM 3");
        mRowsAdapter.add(new ListRow(gridItemPresenterHeader, gridRowAdapter));

        /* set */
        setAdapter(mRowsAdapter);
    }

 最終的に、MainFragment の全ソースコードはこんな感じになる。

package com.corochann.androidtvapptutorial;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.Presenter;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.TextView;

/**
 * Created by corochann on 2015/06/28.
 */
public class MainFragment extends BrowseFragment {
    private static final String TAG = MainFragment.class.getSimpleName();

    private ArrayObjectAdapter mRowsAdapter;
    private static final int GRID_ITEM_WIDTH = 300;
    private static final int GRID_ITEM_HEIGHT = 200;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        Log.i(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);

        setupUIElements();

        loadRows();
    }

    private void setupUIElements() {
        // setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.videos_by_google_banner));
        setTitle("Hello Android TV!"); // Badge, when set, takes precedent
        // over title
        setHeadersState(HEADERS_ENABLED);
        setHeadersTransitionOnBackEnabled(true);

        // set fastLane (or headers) background color
        setBrandColor(getResources().getColor(R.color.fastlane_background));
        // set search icon color
        setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
    }

    private void loadRows() {
        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());

        /* GridItemPresenter */
        HeaderItem gridItemPresenterHeader = new HeaderItem(0, "GridItemPresenter");

        GridItemPresenter mGridPresenter = new GridItemPresenter();
        ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
        gridRowAdapter.add("ITEM 1");
        gridRowAdapter.add("ITEM 2");
        gridRowAdapter.add("ITEM 3");
        mRowsAdapter.add(new ListRow(gridItemPresenterHeader, gridRowAdapter));

        /* set */
        setAdapter(mRowsAdapter);
    }

    private class GridItemPresenter extends Presenter {
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent) {
            TextView view = new TextView(parent.getContext());
            view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT));
            view.setFocusable(true);
            view.setFocusableInTouchMode(true);
            view.setBackgroundColor(getResources().getColor(R.color.default_background));
            view.setTextColor(Color.WHITE);
            view.setGravity(Gravity.CENTER);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
            ((TextView) viewHolder.view).setText((String) item);
        }

        @Override
        public void onUnbindViewHolder(ViewHolder viewHolder) {

        }
    }

}

なお、MainFragmentの背景色として

    <color name="default_background">#3d3d3d</color>

をcolor.xmlに追加した。

(III) ビルド・実行

ヘッダーとコンテンツの組み合わせが見えればOK。

GridItemPresenter1

1点注意。ここでは、 Presenterクラスと実際に表示したいアイテム(ただのString)を用意しただけだった。その他のアニメーション(アイテムを選択すると大きくなって表示される)などはすべてSDKのなかで実装されているので、特に手間をかけなくても大丈夫なようになっている。

UIデザイナーでなくても、 Android TV アプリのUIは

ここまでのソースコード: github.

次の章では How to use Presenter and ViewHolder? – Android TV application hands on tutorial 3、 ImageCardView を用いてカードに画像とタイトル・サブタイトルを載せた表示を行う CardPresenter の実装をする。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です