1. Sebelum memulai
Compose untuk TV adalah framework UI terbaru untuk mengembangkan aplikasi yang berjalan di Android TV. Framework ini mendapatkan semua manfaat Jetpack Compose untuk aplikasi TV sehingga mem-build UI yang indah dan fungsional untuk aplikasi Anda menjadi lebih mudah. Beberapa manfaat khusus Compose untuk TV mencakup hal berikut:
- Fleksibilitas. Compose dapat digunakan untuk membuat jenis UI apa pun, mulai dari tata letak yang sederhana hingga animasi yang kompleks. Komponen dapat langsung difungsikan, tetapi juga dapat disesuaikan dan ditata agar sesuai dengan kebutuhan aplikasi Anda.
- Pengembangan yang disederhanakan dan dipercepat. Compose kompatibel dengan kode yang sudah ada dan memungkinkan developer membuat aplikasi dengan lebih sedikit kode.
- Intuitif: Compose menggunakan sintaksis deklaratif yang membuatnya intuitif untuk mengubah UI Anda, serta melakukan debug, memahami, dan meninjau kode Anda.
Kasus penggunaan umum untuk aplikasi TV adalah konsumsi media. Pengguna menjelajahi katalog konten dan memilih konten yang ingin ditonton. Konten dapat berupa film, acara TV, atau podcast. Setelah pengguna memilih konten, mereka mungkin ingin melihat informasi lebih lanjut tentang konten tersebut, seperti deskripsi singkat, durasi pemutaran, dan nama kreator. Dalam codelab ini, Anda akan mempelajari cara menerapkan layar browser katalog dan layar detail dengan Compose untuk TV.
Prasyarat
- Pengalaman dengan sintaksis Kotlin, termasuk lambda.
- Pengalaman dasar dengan Compose. Jika Anda tidak terbiasa dengan Compose, selesaikan codelab Dasar-dasar Jetpack Compose.
- Pengetahuan dasar tentang composable dan pengubah.
Yang Anda build
- Aplikasi pemutar video dengan layar browser katalog dan layar detail.
- Layar browser katalog yang menampilkan daftar video yang dapat dipilih pengguna. Tampilannya akan terlihat seperti gambar berikut:
- Layar detail yang menunjukkan metadata video yang dipilih, seperti judul, deskripsi, dan durasi. Tampilannya akan terlihat seperti gambar berikut:
Yang Anda perlukan
- Versi terbaru Android Studio
2. Memulai persiapan
Untuk mendapatkan kode yang berisi tema dan penyiapan dasar untuk codelab ini, lakukan salah satu hal berikut:
- Clone kode dari repositori GitHub ini:
$ git clone https://github.com/android/tv-codelabs.git
Cabang main
berisi kode awal dan cabang solution
berisi kode solusi.
- Download file
main.zip
, yang berisi kode awal, dan filesolution.zip
, yang berisi kode solusi.
Setelah berhasil mendownload kode, buka folder project IntroductionToComposeForTV di Android Studio. Sekarang Anda siap untuk memulai.
3. Menerapkan layar browser katalog
Layar browser katalog memungkinkan pengguna menjelajahi katalog film. Anda mengimplementasikan browser katalog sebagai fungsi Composable
. Anda dapat menemukan fungsi CatalogBrowser
Composable
dalam file CatalogBrowser.kt
. Anda akan menerapkan layar browser katalog dalam fungsi Composable
ini.
Kode awal memiliki ViewModel yang disebut class CatalogBrowserViewModel
yang memiliki beberapa atribut dan metode untuk mengambil objek Movie
yang mendeskripsikan konten film. Anda mengimplementasikan browser katalog dengan objek Movie
yang diambil.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
Menampilkan nama kategori
Anda dapat mengakses daftar kategori dengan atribut catalogBrowserViewModel.categoryList
yang merupakan alur daftar Category
. Alur ini dikumpulkan sebagai objek Compose State
dengan memanggil metode collectAsState
. Objek Category
memiliki atribut name
, yang merupakan nilai String
yang mewakili nama kategori.
Untuk menampilkan nama kategori, ikuti langkah-langkah berikut:
- Di Android Studio, buka file
CatalogBrowser.kt
kode awal, lalu tambahkan fungsiTvLazyColumn
Composable
ke fungsiCatalogBrowser
Composable
. - Panggil metode
catalogBrowserViewModel.categoryList.collectAsState()
untuk mengumpulkan alur sebagai objekState
. - Deklarasikan
categoryList
sebagai properti yang didelegasikan dari objekState
yang Anda buat di langkah sebelumnya. - Panggil fungsi
items
dengan variabelcategoryList
sebagai parameter. - Panggil fungsi
Text
Composable
dengan nama kategori sebagai parameter yang diteruskan sebagai argumen lambda.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
}
}
}
Menampilkan daftar konten untuk setiap kategori
Objek Category
memiliki atribut lain yang disebut movieList
. Atribut ini adalah daftar objek Movie
yang mewakili film yang termasuk dalam kategori tersebut.
Untuk menampilkan daftar konten untuk setiap kategori, ikuti langkah-langkah berikut:
- Tambahkan fungsi
TvLazyRow
Composable
, lalu teruskan lambda ke fungsi tersebut. - Di lambda, panggil fungsi
items
dengancategory
.Nilai atributmovieList
, lalu teruskan lambda ke nilai tersebut. - Di lambda yang diteruskan ke fungsi
items
, panggil fungsiMovieCard
Composable
dengan objekMovie
.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow {
items(category.movieList) {movie ->
MovieCard(movie = movie)
}
}
}
}
}
Opsional: Menyesuaikan tata letak
- Untuk menetapkan jarak antar-kategori, teruskan objek
Arrangement
ke fungsiTvLazyColumn
Composable
dengan parameterverticalArrangement
. ObjekArrangement
dibuat dengan memanggil metodeArrangement#spacedBy
. - Untuk menetapkan jarak antar-kartu film, teruskan objek
Arrangement
ke fungsiTvLazyRow
Composable
dengan parameterhorizontalArrangement
. - Untuk menetapkan indentasi ke kolom, teruskan objek
PaddingValue
dengan parametercontentPadding
.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie)
}
}
}
}
}
4. Menerapkan layar detail
Layar detail menampilkan detail film yang dipilih. Ada fungsi Details
Composable
dalam file Details.kt
. Anda akan menambahkan kode ke fungsi ini untuk menerapkan layar detail.
Details.kt
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.tv.material3.ExperimentalTvMaterial3Api
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
Menampilkan judul film, nama studio, dan deskripsi
Objek Movie
memiliki tiga atribut string berikut sebagai metadata film:
title
. Judul film.studio
. Nama studio yang memproduksi film.description
. Ringkasan singkat film.
Untuk menampilkan metadata ini di layar detail, ikuti langkah-langkah berikut:
- Tambahkan fungsi
Column
Composable
, lalu tetapkan jarak vertikal 32 dp dan horizontal 48 dp di sekitar kolom dengan objekModifier
yang dibuat oleh metodeModifier.padding
. - Tambahkan fungsi
Composable
Text
untuk menampilkan judul film. - Tambahkan fungsi
Text
Composable
untuk menampilkan nama studio. - Tambahkan fungsi
Composable
Text
untuk menampilkan deskripsi film.
Details.kt
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(text = movie.title)
Text(text = movie.studio)
Text(text = movie.title)
}
}
Objek Modifier
yang ditentukan dalam parameter fungsi Details
Composable
digunakan dalam tugas berikutnya.
Menampilkan gambar latar yang terkait dengan objek Movie
tertentu
Objek Movie
memiliki atribut backgroundImageUrl
yang menunjukkan lokasi gambar latar untuk film yang dijelaskan oleh objek.
Agar gambar latar untuk film tertentu dapat ditampilkan, ikuti langkah-langkah berikut:
- Tambahkan fungsi
Box
Composable
sebagai wrapper fungsiColumn
Composable
dengan objekmodifier
yang diteruskan melalui fungsiDetails
Composable
. - Dalam fungsi
Box
Composable
, panggil metodefillMaxSize
objekmodifier
untuk membuat fungsiBox
Composable
mengisi ukuran maksimum yang dapat dialokasikan keDetails
fungsiComposable
. - Tambahkan fungsi
AsyncImage
Composable
dengan parameter berikut ke fungsiBox
Composable
:
- Setel nilai atribut
backgroundImageUrl
dari objekMovie
yang diberikan ke parametermodel
. - Teruskan
null
ke parametercontentDescription
.
- Teruskan objek
ContentScale.Crop
ke parametercontentScale
. Untuk melihat opsiContentScale
yang berbeda, lihat Skala konten. - Teruskan nilai return metode
Modifier.fillMaxSize
ke parametermodifier
. - Tetapkan jarak horizontal 32 dp dan jarak horizontal 48 dp ke kolom dengan menetapkan objek
Modifier
yang dibuat dengan memanggil metodeModifier.padding
.
Details.kt
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(
text = movie.title,
)
Text(
text = movie.studio,
)
Text(
text = movie.title,
)
}
}
}
Lihat objek MaterialTheme
untuk penerapan tema yang konsisten
Objek MaterialTheme
berisi fungsi untuk mereferensikan nilai tema saat ini, seperti yang ada di class Typography
dan [ColorScheme
][ColorScheme].
Untuk melihat objek MaterialTheme
agar temanya konsisten, ikuti langkah-langkah berikut:
- Tetapkan properti
MaterialTheme.typography.headlineLarge
ke gaya teks judul film. - Setel properti
MaterialTheme.typography.headlineMedium
ke gaya teks dari dua fungsiText
Composable
lainnya. - Tetapkan properti
MaterialTheme.colorScheme.background
ke warna latar belakang fungsiColumn
Composable
dengan metodeModifier.background
.
[ColorScheme]: /reference/kotlin/androidx/tv/material3/ColorScheme)
Details.kt
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(
text = movie.title,
style = MaterialTheme.typography.headlineLarge,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.headlineMedium,
)
Text(
text = movie.title,
style = MaterialTheme.typography.headlineMedium,
)
}
}
}
5. Menambahkan navigasi antar-layar
Sekarang Anda memiliki browser katalog dan layar detail. Setelah pengguna memilih konten di layar browser katalog, layar harus bertransisi ke layar detail. Untuk memungkinkan hal ini, gunakan pengubah clickable
untuk menambahkan pemroses event
ke fungsi MovieCard
Composable
. Saat tombol tengah dari tombol arah ditekan, metode CatalogBrowserViewModel#showDetails
dipanggil dengan objek film yang terkait dengan fungsi MovieCard
Composable
sebagai argumen.
- Buka file
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Teruskan fungsi lambda ke fungsi
MovieCard
Composable
dengan parameteronClick
. - Panggil callback
onMovieSelected
dengan objek film yang terkait dengan fungsiMovieCard
Composable
.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
6. Menambahkan carousel ke layar browser katalog untuk menandai konten unggulan
Carousel adalah komponen UI yang umum diadaptasikan, yang otomatis mengupdate slide setelah durasi yang ditentukan. Atribut ini biasanya digunakan untuk menyoroti konten unggulan.
Untuk menambahkan carousel ke layar katalog guna menyoroti film dalam daftar konten unggulan, ikuti langkah-langkah berikut:
- Buka file
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Panggil fungsi
item
untuk menambahkan item ke fungsiTvLazyColumn
Composable
. - Deklarasikan
featuredMovieList
sebagai properti yang didelegasikan dalam lambda yang diteruskan ke fungsiitem
, lalu tetapkan objekState
yang akan didelegasikan, yang dikumpulkan dari atributcatalogBrowserViewModel.featuredMovieList
. - Panggil fungsi
Carousel
Composable
di dalam fungsiitem
, lalu teruskan parameter berikut:
- Ukuran variabel
featuredMovieList
melalui parameterslideCount
. - Objek
Modifier
untuk menentukan ukuran carousel dengan metodeModifier.fillMaxWidth
danModifier.height
. FungsiCarousel
Composable
menggunakan tinggi 376 dp dengan meneruskan nilai376.dp
ke metodeModifier.height
. - Lambda yang dipanggil dengan nilai bilangan bulat yang menunjukkan indeks item carousel yang terlihat.
- Ambil objek
Movie
dari variabelfeaturedMovieList
dan nilai indeks yang diberikan. - Tambahkan fungsi
CarouselSlide
Composable
ke fungsiCarousel
Composable
. - Tambahkan fungsi
Text
Composable
ke fungsiCarouselSlide
Composable
untuk menampilkan judul film.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp)
) { indexOfCarouselSlide ->
val featuredMovie =
featuredMovieList[indexOfCarouselSlide]
CarouselSlide {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Menampilkan gambar latar
Fungsi CarouselSlide
Composable
dapat menggunakan lambda lain untuk menentukan cara latar belakang fungsi CarouselSlide
Composable
ditampilkan.
Untuk menampilkan gambar latar, ikuti langkah-langkah berikut:
- Teruskan lambda ke fungsi
CarouselSlide
Composable
dengan parameterbackground
. - Panggil fungsi
AsyncImage
Composable
untuk memuat gambar latar yang terkait dengan objekMovie
ke latar belakang fungsiCarouselSlide
Composable
. - Perbarui posisi dan gaya teks fungsi
Text
Composable
dalam fungsiCarouselSlide
Composable
untuk visibilitas yang lebih baik. - Setel placeholder ke fungsi
AsyncImage
Composable
untuk menghindari pergeseran tata letak. Kode awal memiliki placeholder sebagai drawable yang dapat Anda referensikan denganR.drawable.placeholder
.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.R
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
CarouselSlide(
background = {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
},
) {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Menambahkan transisi layar ke layar detail
Anda dapat mengizinkan pengguna mengklik fungsi CarouselSlide
Composable
.
Untuk mengizinkan pengguna melihat detail film di item carousel yang terlihat di layar detail, ikuti langkah-langkah berikut:
- Teruskan nilai return metode
Modifier.clickable
ke fungsiCarouselSlide
Composable
melalui parametermodifier
. - Panggil fungsi
onMovieSelected
dengan objekMovie
untuk fungsiCarouselSlide
Composable
yang terlihat di lambda yang diteruskan ke metodeModifier.clickable
.
CatalogBrowser.kt
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.R
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
CarouselSlide(
background = {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
},
modifier = Modifier.clickable { onMovieSelected(featuredMovie) }
) {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
7. Mendapatkan kode solusi
Untuk mendownload kode solusi untuk codelab ini, lakukan salah satu hal berikut:
- Klik tombol berikut untuk mendownloadnya sebagai file zip, lalu ekstrak dan buka di Android Studio.
- Ambil dengan Git:
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
8. Selamat.
Selamat! Anda telah mempelajari dasar-dasar Compose untuk TV:
- Cara menerapkan layar untuk menampilkan daftar konten dengan menggabungkan TvLazyColumn dan TvLazyLow.
- Implementasi layar dasar untuk menampilkan detail konten.
- Cara menambahkan transisi layar antara dua layar.