นำเสนอมุมมองการ์ด
จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน
บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ
สร้างแอปที่ดีขึ้นด้วย Compose
สร้าง UI ที่สวยงามด้วยโค้ดเพียงเล็กน้อยโดยใช้ Jetpack Compose สำหรับระบบปฏิบัติการ Android TV
คู่มือนี้มุ่งเน้นที่การสร้างมุมมองการ์ดสำหรับรายการสื่อและการนำเสนอใน
Browse Fragment โดยใช้ชุดเครื่องมือ UI ของ Leanback ที่เลิกใช้งานแล้ว การใช้งาน
แคตตาล็อกเบราว์เซอร์ใน Browse Fragment มีรายละเอียดอยู่ใน
คำแนะนำเกี่ยวกับ Browse Fragment
คลาส BaseCardView
และคลาสย่อยจะแสดงข้อมูลเมตาที่เชื่อมโยงกับรายการสื่อ คลาส
ImageCardView
ที่ใช้ในบทเรียนนี้จะแสดงรูปภาพสำหรับเนื้อหาพร้อมกับชื่อของรายการสื่อ
ดูตัวอย่างการใช้งานใน
แอปตัวอย่าง Leanback
ที่เลิกใช้งานแล้วด้วย
รูปที่ 1 มุมมองการ์ดรูปภาพของแอปตัวอย่าง Leanback เมื่อเลือก
สร้างผู้นำเสนอการ์ด
Presenter
สร้างมุมมองและเชื่อมโยงออบเจ็กต์กับมุมมองเหล่านั้น
ตามต้องการ ใน Fragment การเรียกดูที่แอปแสดงเนื้อหาต่อผู้ใช้ คุณสร้าง
Presenter
สำหรับการ์ดเนื้อหาและส่งไปยัง Adapter
ที่เพิ่มเนื้อหาลงในหน้าจอ ในโค้ดต่อไปนี้ ระบบจะสร้าง CardPresenter
ในแฮนเดิล onLoadFinished()
ของแฮนเดิล LoaderManager
Kotlin
override fun onLoadFinished(loader: Loader<HashMap<String, List<Movie>>>, data: HashMap<String, List<Movie>>) {
rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
val cardPresenter = CardPresenter()
var i = 0L
data.entries.forEach { entry ->
val listRowAdapter = ArrayObjectAdapter(cardPresenter).apply {
entry.value.forEach { movie ->
add(movie)
}
}
val header = HeaderItem(i, entry.key)
i++
rowsAdapter.add(ListRow(header, listRowAdapter))
}
val gridHeader = HeaderItem(i, getString(R.string.more_samples))
val gridRowAdapter = ArrayObjectAdapter(GridItemPresenter()).apply {
add(getString(R.string.grid_view))
add(getString(R.string.error_fragment))
add(getString(R.string.personal_settings))
}
rowsAdapter.add(ListRow(gridHeader, gridRowAdapter))
adapter = rowsAdapter
updateRecommendations()
}
Java
@Override
public void onLoadFinished(Loader<HashMap<String, List<Movie>>> arg0,
HashMap<String, List<Movie>> data) {
rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
CardPresenter cardPresenter = new CardPresenter();
int i = 0;
for (Map.Entry<String, List<Movie>> entry : data.entrySet()) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
List<Movie> list = entry.getValue();
for (int j = 0; j < list.size(); j++) {
listRowAdapter.add(list.get(j));
}
HeaderItem header = new HeaderItem(i, entry.getKey());
i++;
rowsAdapter.add(new ListRow(header, listRowAdapter));
}
HeaderItem gridHeader = new HeaderItem(i, getString(R.string.more_samples));
GridItemPresenter gridPresenter = new GridItemPresenter();
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
gridRowAdapter.add(getString(R.string.grid_view));
gridRowAdapter.add(getString(R.string.error_fragment));
gridRowAdapter.add(getString(R.string.personal_settings));
rowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
setAdapter(rowsAdapter);
updateRecommendations();
}
สร้างมุมมองการ์ด
ในขั้นตอนนี้ คุณจะสร้างตัวแสดงการ์ดที่มีตัวยึดมุมมองสําหรับมุมมองการ์ดที่อธิบายรายการเนื้อหาสื่อ โปรดทราบว่าผู้บรรยายแต่ละคนต้องสร้างประเภทมุมมองเพียงประเภทเดียว หากมีมุมมองการ์ด 2 ประเภท คุณจะต้องมี CardPresenter 2 รายการ
ใน Presenter
ให้ใช้
onCreateViewHolder()
การเรียกกลับที่สร้างตัวยึดมุมมองซึ่งใช้เพื่อแสดงรายการเนื้อหาได้
Kotlin
private const val CARD_WIDTH = 313
private const val CARD_HEIGHT = 176
class CardPresenter : Presenter() {
private lateinit var mContext: Context
private lateinit var defaultCardImage: Drawable
override fun onCreateViewHolder(parent: ViewGroup): Presenter.ViewHolder {
mContext = parent.context
defaultCardImage = mContext.resources.getDrawable(R.drawable.movie)
...
Java
@Override
public class CardPresenter extends Presenter {
private Context context;
private static int CARD_WIDTH = 313;
private static int CARD_HEIGHT = 176;
private Drawable defaultCardImage;
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
context = parent.getContext();
defaultCardImage = context.getResources().getDrawable(R.drawable.movie);
...
ในonCreateViewHolder()
ให้สร้างมุมมองการ์ดสำหรับรายการเนื้อหา ตัวอย่างต่อไปนี้ใช้
ImageCardView
เมื่อเลือกการ์ดแล้ว ลักษณะการทำงานเริ่มต้นจะขยายการ์ดให้มีขนาดใหญ่ขึ้น หากต้องการกำหนดสีอื่นสำหรับ
การ์ดที่เลือก ให้เรียกใช้ setSelected()
ตามที่แสดงที่นี่
Kotlin
...
val cardView = object : ImageCardView(context) {
override fun setSelected(selected: Boolean) {
val selected_background = context.resources.getColor(R.color.detail_background)
val default_background = context.resources.getColor(R.color.default_background)
val color = if (selected) selected_background else default_background
findViewById<View>(R.id.info_field).setBackgroundColor(color)
super.setSelected(selected)
}
}
...
Java
...
ImageCardView cardView = new ImageCardView(context) {
@Override
public void setSelected(boolean selected) {
int selected_background = context.getResources().getColor(R.color.detail_background);
int default_background = context.getResources().getColor(R.color.default_background);
int color = selected ? selected_background : default_background;
findViewById(R.id.info_field).setBackgroundColor(color);
super.setSelected(selected);
}
};
...
เมื่อผู้ใช้เปิดแอป Presenter.ViewHolder
จะแสดงออบเจ็กต์ CardView
สำหรับรายการเนื้อหาของคุณ คุณต้องตั้งค่าเหล่านี้เพื่อรับโฟกัสจากตัวควบคุม D-pad โดยการเรียก setFocusable(true)
และ setFocusableInTouchMode(true)
ดังที่แสดงในโค้ดต่อไปนี้
Kotlin
...
cardView.isFocusable = true
cardView.isFocusableInTouchMode = true
return ViewHolder(cardView)
}
Java
...
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
return new ViewHolder(cardView);
}
เมื่อผู้ใช้เลือก ImageCardView
ระบบจะขยาย
เพื่อแสดงพื้นที่ข้อความที่มีสีพื้นหลังที่คุณระบุ ดังที่แสดงในรูปที่ 1
ตัวอย่างเนื้อหาและโค้ดในหน้าเว็บนี้ขึ้นอยู่กับใบอนุญาตที่อธิบายไว้ในใบอนุญาตการใช้เนื้อหา Java และ OpenJDK เป็นเครื่องหมายการค้าหรือเครื่องหมายการค้าจดทะเบียนของ Oracle และ/หรือบริษัทในเครือ
อัปเดตล่าสุด 2025-09-06 UTC
[[["เข้าใจง่าย","easyToUnderstand","thumb-up"],["แก้ปัญหาของฉันได้","solvedMyProblem","thumb-up"],["อื่นๆ","otherUp","thumb-up"]],[["ไม่มีข้อมูลที่ฉันต้องการ","missingTheInformationINeed","thumb-down"],["ซับซ้อนเกินไป/มีหลายขั้นตอนมากเกินไป","tooComplicatedTooManySteps","thumb-down"],["ล้าสมัย","outOfDate","thumb-down"],["ปัญหาเกี่ยวกับการแปล","translationIssue","thumb-down"],["ตัวอย่าง/ปัญหาเกี่ยวกับโค้ด","samplesCodeIssue","thumb-down"],["อื่นๆ","otherDown","thumb-down"]],["อัปเดตล่าสุด 2025-09-06 UTC"],[],[],null,["Build better with Compose \nCreate beautiful UIs with minimal code using Jetpack Compose for Android TV OS. \n[Compose for TV →](/training/tv/playback/compose) \n| **Warning:** The Leanback library is deprecated. Use [Jetpack Compose for\n| Android TV OS](/training/tv/playback/compose) instead.\n\nThis guide focuses on creating the card views for your media items and presenting them in the\nbrowse fragment using the deprecated Leanback UI toolkit. The implementation\nof the catalog browser in a browse fragment is detailed in the\n[Browse Fragment guide](/training/tv/playback/leanback/browse).\n\nThe [BaseCardView](/reference/androidx/leanback/widget/BaseCardView)\nclass and subclasses display the metadata associated with a media item. The\n[ImageCardView](/reference/androidx/leanback/widget/ImageCardView)\nclass used in this lesson displays an image for the content along with the media item's title.\n\nSee also the sample implementation in the deprecated\n[Leanback sample app](https://github.com/android/tv-samples/tree/main/Leanback)\n.\n\n**Figure 1.** The Leanback sample app image card view when selected.\n\nCreate a card presenter\n\nA [Presenter](/reference/androidx/leanback/widget/Presenter) generates views and binds objects to them\non demand. In the browse fragment where your app presents its content to the user, you create a\n`Presenter` for the content cards and pass it to the adapter\nthat adds the content to the screen. In the following code, the `CardPresenter` is created\nin the [onLoadFinished()](/reference/androidx/loader/app/LoaderManager.LoaderCallbacks#onLoadFinished(androidx.loader.content.Loader%3CD%3E,D))\ncallback of the [LoaderManager](/reference/androidx/loader/app/LoaderManager): \n\nKotlin \n\n```kotlin\noverride fun onLoadFinished(loader: Loader\u003cHashMap\u003cString, List\u003cMovie\u003e\u003e\u003e, data: HashMap\u003cString, List\u003cMovie\u003e\u003e) {\n rowsAdapter = ArrayObjectAdapter(ListRowPresenter())\n val cardPresenter = CardPresenter()\n\n var i = 0L\n\n data.entries.forEach { entry -\u003e\n val listRowAdapter = ArrayObjectAdapter(cardPresenter).apply {\n entry.value.forEach { movie -\u003e\n add(movie)\n }\n }\n\n val header = HeaderItem(i, entry.key)\n i++\n rowsAdapter.add(ListRow(header, listRowAdapter))\n }\n\n val gridHeader = HeaderItem(i, getString(R.string.more_samples))\n\n val gridRowAdapter = ArrayObjectAdapter(GridItemPresenter()).apply {\n add(getString(R.string.grid_view))\n add(getString(R.string.error_fragment))\n add(getString(R.string.personal_settings))\n }\n rowsAdapter.add(ListRow(gridHeader, gridRowAdapter))\n\n adapter = rowsAdapter\n\n updateRecommendations()\n}\n```\n\nJava \n\n```java\n@Override\npublic void onLoadFinished(Loader\u003cHashMap\u003cString, List\u003cMovie\u003e\u003e\u003e arg0,\n HashMap\u003cString, List\u003cMovie\u003e\u003e data) {\n\n rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());\n CardPresenter cardPresenter = new CardPresenter();\n\n int i = 0;\n\n for (Map.Entry\u003cString, List\u003cMovie\u003e\u003e entry : data.entrySet()) {\n ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);\n List\u003cMovie\u003e list = entry.getValue();\n\n for (int j = 0; j \u003c list.size(); j++) {\n listRowAdapter.add(list.get(j));\n }\n HeaderItem header = new HeaderItem(i, entry.getKey());\n i++;\n rowsAdapter.add(new ListRow(header, listRowAdapter));\n }\n\n HeaderItem gridHeader = new HeaderItem(i, getString(R.string.more_samples));\n\n GridItemPresenter gridPresenter = new GridItemPresenter();\n ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);\n gridRowAdapter.add(getString(R.string.grid_view));\n gridRowAdapter.add(getString(R.string.error_fragment));\n gridRowAdapter.add(getString(R.string.personal_settings));\n rowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));\n\n setAdapter(rowsAdapter);\n\n updateRecommendations();\n}\n```\n\nCreate a card view\n\nIn this step, you build the card presenter with a view holder for the card view that describes\nyour media content items. Note that each presenter must create only one view type. If you have two\ncard view types, then you need two card presenters.\n\nIn the [Presenter](/reference/androidx/leanback/widget/Presenter), implement an\n[onCreateViewHolder()](/reference/androidx/leanback/widget/Presenter#onCreateViewHolder(android.view.ViewGroup))\ncallback that creates a view holder that can be used to display a content item: \n\nKotlin \n\n```kotlin\nprivate const val CARD_WIDTH = 313\nprivate const val CARD_HEIGHT = 176\n\nclass CardPresenter : Presenter() {\n\n private lateinit var mContext: Context\n private lateinit var defaultCardImage: Drawable\n\n override fun onCreateViewHolder(parent: ViewGroup): Presenter.ViewHolder {\n mContext = parent.context\n defaultCardImage = mContext.resources.getDrawable(R.drawable.movie)\n ...\n```\n\nJava \n\n```java\n@Override\npublic class CardPresenter extends Presenter {\n\n private Context context;\n private static int CARD_WIDTH = 313;\n private static int CARD_HEIGHT = 176;\n private Drawable defaultCardImage;\n\n @Override\n public ViewHolder onCreateViewHolder(ViewGroup parent) {\n context = parent.getContext();\n defaultCardImage = context.getResources().getDrawable(R.drawable.movie);\n...\n```\n\nIn the [onCreateViewHolder()](/reference/androidx/leanback/widget/Presenter#onCreateViewHolder(android.view.ViewGroup)) method,\ncreate a card view for content items. The following sample uses an\n[ImageCardView](/reference/androidx/leanback/widget/ImageCardView).\n\nWhen a card is selected, the default behavior expands it to a larger size. If you want to designate\na different color for the selected card, call [setSelected()](/reference/androidx/leanback/widget/BaseCardView#setSelected(boolean))\nas shown here: \n\nKotlin \n\n```kotlin\n ...\n val cardView = object : ImageCardView(context) {\n override fun setSelected(selected: Boolean) {\n val selected_background = context.resources.getColor(R.color.detail_background)\n val default_background = context.resources.getColor(R.color.default_background)\n val color = if (selected) selected_background else default_background\n findViewById\u003cView\u003e(R.id.info_field).setBackgroundColor(color)\n super.setSelected(selected)\n }\n }\n ...\n```\n\nJava \n\n```java\n...\n ImageCardView cardView = new ImageCardView(context) {\n @Override\n public void setSelected(boolean selected) {\n int selected_background = context.getResources().getColor(R.color.detail_background);\n int default_background = context.getResources().getColor(R.color.default_background);\n int color = selected ? selected_background : default_background;\n findViewById(R.id.info_field).setBackgroundColor(color);\n super.setSelected(selected);\n }\n };\n...\n```\n\nWhen the user opens your app, the [Presenter.ViewHolder](/reference/androidx/leanback/widget/Presenter.ViewHolder)\ndisplays the `CardView` objects for your content items. You need to set these to receive\nfocus from the D-pad controller by calling [setFocusable(true)](/reference/android/view/View#setFocusable(boolean))\nand [setFocusableInTouchMode(true)](/reference/android/view/View#setFocusableInTouchMode(boolean)),\nas shown in the following code: \n\nKotlin \n\n```kotlin\n ...\n cardView.isFocusable = true\n cardView.isFocusableInTouchMode = true\n return ViewHolder(cardView)\n}\n```\n\nJava \n\n```java\n...\n cardView.setFocusable(true);\n cardView.setFocusableInTouchMode(true);\n return new ViewHolder(cardView);\n}\n```\n\nWhen the user selects the [ImageCardView](/reference/androidx/leanback/widget/ImageCardView), it expands\nto reveal its text area with the background color you specify, as shown in figure 1."]]