詳細ビューを作成する

Leanback androidx ライブラリが提供するメディア ブラウジング インターフェース クラスには、説明やレビューなどのメディア アイテムに関する追加情報を表示するためのクラスや、アイテムの購入やコンテンツ再生など、アイテムに対するアクションを実行するためのクラスが含まれています。

このレッスンでは、メディア アイテムの詳細に関するプレゼンター クラスを作成する方法と、DetailsFragment クラスを拡張してユーザーがメディア アイテムを選択した場合に表示する詳細ビューを実装する方法について説明します。

注: 次の実装例では、追加的なアクティビティを使用して DetailsFragment を含めています。ただし、フラグメント トランザクションを使用すると、現在の BrowseFragment を同じアクティビティにある DetailsFragment で置き換えることにより、2 つ目のアクティビティの作成を回避できます。フラグメント トランザクションの使用については、フラグメントを使用したダイナミックな UI のビルドを参照してください。

詳細プレゼンターをビルドする

Leanback ライブラリが提供するメディア ブラウジングのフレームワークでは、プレゼンター オブジェクトを使用して、メディア アイテムの詳細を含むスクリーン上のデータ表示を制御します。このメディア アイテムの詳細に関するプレゼンターのほぼ完全な実装を目的に、フレームワークでは AbstractDetailsDescriptionPresenter クラスを提供しています。これにより、次のコードサンプルのように、onBindDescription() メソッドを実装してビュー フィールドをデータ オブジェクトにバインドするだけでよくなります。

Kotlin

    class DetailsDescriptionPresenter : AbstractDetailsDescriptionPresenter() {

        override fun onBindDescription(viewHolder: AbstractDetailsDescriptionPresenter.ViewHolder, itemData: Any) {
            val details = itemData as MyMediaItemDetails
            // In a production app, the itemData object contains the information
            // needed to display details for the media item:
            // viewHolder.title.text = details.shortTitle

            // Here we provide static data for testing purposes:
            viewHolder.apply {
                title.text = itemData.toString()
                subtitle.text = "2014   Drama   TV-14"
                body.text = ("Lorem ipsum dolor sit amet, consectetur "
                        + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
                        + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
                        + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
                        + "commodo consequat.")
            }
        }
    }
    

Java

    public class DetailsDescriptionPresenter
            extends AbstractDetailsDescriptionPresenter {

        @Override
        protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
            MyMediaItemDetails details = (MyMediaItemDetails) itemData;
            // In a production app, the itemData object contains the information
            // needed to display details for the media item:
            // viewHolder.getTitle().setText(details.getShortTitle());

            // Here we provide static data for testing purposes:
            viewHolder.getTitle().setText(itemData.toString());
            viewHolder.getSubtitle().setText("2014   Drama   TV-14");
            viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
                    + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
                    + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
                    + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
                    + "commodo consequat.");
        }
    }
    

詳細フラグメントを拡張する

メディア アイテムの詳細を表示するために DetailsFragment クラスを使用する場合、そのクラスを拡張すると、メディア アイテムのプレビュー画像やアクションなどの追加コンテンツを提供できます。関連するメディア アイテムのリストといった追加コンテンツも提供できます。

次のサンプルコードでは、前述のプレゼンター クラスを使用して、表示中のメディア アイテムのプレビュー画像やアクションを追加する方法を示しています。この例では、詳細リストの下に表示される関連するメディア アイテムの行も追加しています。

Kotlin

    private const val TAG = "MediaItemDetailsFragment"

    class MediaItemDetailsFragment : DetailsFragment() {
        private lateinit var rowsAdapter: ArrayObjectAdapter

        override fun onCreate(savedInstanceState: Bundle?) {
            Log.i(TAG, "onCreate")
            super.onCreate(savedInstanceState)

            buildDetails()
        }

        private fun buildDetails() {
            val selector = ClassPresenterSelector().apply {
                // Attach your media item details presenter to the row presenter:
                FullWidthDetailsOverviewRowPresenter(DetailsDescriptionPresenter()).also {
                    addClassPresenter(DetailsOverviewRow::class.java, it)
                }
                addClassPresenter(ListRow::class.java, ListRowPresenter())
            }
            rowsAdapter = ArrayObjectAdapter(selector)

            val res = activity.resources
            val detailsOverview = DetailsOverviewRow("Media Item Details").apply {

                // Add images and action buttons to the details view
                imageDrawable = res.getDrawable(R.drawable.jelly_beans)
                addAction(Action(1, "Buy $9.99"))
                addAction(Action(2, "Rent $2.99"))
            }
            rowsAdapter.add(detailsOverview)

            // Add a Related items row
            val listRowAdapter = ArrayObjectAdapter(StringPresenter()).apply {
                add("Media Item 1")
                add("Media Item 2")
                add("Media Item 3")
            }
            val header = HeaderItem(0, "Related Items")
            rowsAdapter.add(ListRow(header, listRowAdapter))

            adapter = rowsAdapter
        }
    }
    

Java

    public class MediaItemDetailsFragment extends DetailsFragment {
        private static final String TAG = "MediaItemDetailsFragment";
        private ArrayObjectAdapter rowsAdapter;

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

            buildDetails();
        }

        private void buildDetails() {
            ClassPresenterSelector selector = new ClassPresenterSelector();
            // Attach your media item details presenter to the row presenter:
            FullWidthDetailsOverviewRowPresenter rowPresenter =
                new FullWidthDetailsOverviewRowPresenter(
                    new DetailsDescriptionPresenter());

            selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
            selector.addClassPresenter(ListRow.class,
                    new ListRowPresenter());
            rowsAdapter = new ArrayObjectAdapter(selector);

            Resources res = getActivity().getResources();
            DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
                    "Media Item Details");

            // Add images and action buttons to the details view
            detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
            detailsOverview.addAction(new Action(1, "Buy $9.99"));
            detailsOverview.addAction(new Action(2, "Rent $2.99"));
            rowsAdapter.add(detailsOverview);

            // Add a Related items row
            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
                    new StringPresenter());
            listRowAdapter.add("Media Item 1");
            listRowAdapter.add("Media Item 2");
            listRowAdapter.add("Media Item 3");
            HeaderItem header = new HeaderItem(0, "Related Items", null);
            rowsAdapter.add(new ListRow(header, listRowAdapter));

            setAdapter(rowsAdapter);
        }
    }
    

詳細アクティビティを作成する

DetailsFragment のようなフラグメントを表示に使用するには、これをアクティビティ内に含める必要があります。ブラウズ アクティビティとは別に詳細ビューのアクティビティを作成すると、Intent を使用して詳細ビューを呼び出すことができます。ここでは、メディア アイテムの詳細ビューの実装を含んだアクティビティのビルド方法を説明します。

次のように DetailsFragment の実装を参照するレイアウトをビルドして、詳細アクティビティの作成を開始します。

    <!-- file: res/layout/details.xml -->

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"
        android:id="@+id/details_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
    

次に、前述のコードサンプルのレイアウトを使用して、アクティビティ クラスを作成します。

Kotlin

    class DetailsActivity : Activity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.details)
        }
    }
    

Java

    public class DetailsActivity extends Activity
    {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.details);
        }
    }
    

最後に、この新しいアクティビティをマニフェストに追加します。ユーザー インターフェースがメディア ブラウズ アクティビティと一致するように Leanback テーマを適用してください。

    <application>
      ...

      <activity android:name=".DetailsActivity"
        android:exported="true"
        android:theme="@style/Theme.Leanback"/>

    </application>
    

クリックされたアイテムのリスナーを定義する

DetailsFragment を実装したら、ユーザーがメディア アイテムをクリックしたときに詳細ビューに移動するようにメインのメディア ブラウジング ビューを変更します。この動作を有効にするには、アイテム詳細アクティビティを起動するインテントの起動元の BrowseFragmentOnItemViewClickedListener オブジェクトを追加します。

次の例は、ユーザーがメインのメディア ブラウジング アクティビティのメディア アイテムをクリックしたときに詳細ビューを起動するようにリスナーを実装する方法を示しています。

Kotlin

    class BrowseMediaActivity : AppCompatActivity() {
        ...

        override fun onCreate(savedInstanceState: Bundle?) {
            ...

            // create the media item rows
            buildRowsAdapter()

            // add a listener for selected items
            browseFragment.onItemViewClickedListener = OnItemViewClickedListener { _, item, _, _ ->
                println("Media Item clicked: ${item}")
                val intent = Intent(this@BrowseMediaActivity, DetailsActivity::class.java).apply {
                    // pass the item information
                    extras.putLong("id", item.getId())
                }
                startActivity(intent)
            }
        }
    }
    

Java

    public class BrowseMediaActivity extends Activity {
        ...

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...

            // create the media item rows
            buildRowsAdapter();

            // add a listener for selected items
            browseFragment.OnItemViewClickedListener(
                new OnItemViewClickedListener() {
                    @Override
                    public void onItemClicked(Object item, Row row) {
                        System.out.println("Media Item clicked: " + item.toString());
                        Intent intent = new Intent(BrowseMediaActivity.this,
                                DetailsActivity.class);
                        // pass the item information
                        intent.getExtras().putLong("id", item.getId());
                        startActivity(intent);
                    }
                });
        }
    }