1. 始める前に
Compose for TV は、Android TV で動作するアプリを開発するための最新の UI フレームワークです。TV アプリ向け Jetpack Compose のすべてのメリットを活用できるため、アプリの美しく機能性の高い UI の作成が容易になります。Compose for TV の具体的なメリットは次のとおりです。
- 柔軟性 Compose を使用すると、シンプルなレイアウトから複雑なアニメーションまで、あらゆる種類の UI を作成できます。コンポーネントはすぐに使用できますが、アプリのニーズに合わせたカスタマイズやスタイル設定も可能です。
- 開発の簡素化と加速Compose は既存のコードと互換性があるため、デベロッパーは少ないコードでアプリを構築できます。
- 直感的: Compose では、UI の変更とコードのデバッグ、理解、レビューを直感的に行える宣言型構文が使用されています。
TV アプリの一般的なユースケースはメディア消費です。ユーザーはコンテンツ カタログを参照して、視聴するコンテンツを選択します。コンテンツには、ムービー、テレビ番組、ポッドキャストなどがあります。ユーザーがコンテンツの選択を終えると、簡単な説明、再生時間、クリエイターの名前などの詳細情報が表示される可能性があります。この Codelab では、Compose for TV を使用してカタログ ブラウザ画面と詳細画面を実装する方法を学びます。
前提条件
- ラムダを含む Kotlin 構文の使用経験。
- Compose に関する基本的な経験。Compose に慣れていない場合は、Jetpack Compose の基本の Codelab を修了してください。
- コンポーザブルと修飾子に関する基本的な知識。
- 次のいずれかのデバイス(サンプルアプリの実行用):
- Android TV デバイス
- プロファイルが TV デバイス定義カテゴリに属する Android 仮想デバイス
構築内容
- カタログ ブラウザ画面と詳細画面を備えた動画プレーヤー アプリ。
- ユーザーが選択できる動画の一覧が表示されるカタログ ブラウザ画面。次の画像のように表示されます。
- タイトル、説明、長さなど、選択した動画のメタデータを表示する詳細画面。次の画像のように表示されます。
必要なもの
- Android Studio の最新バージョン
- TV デバイス カテゴリに属する Android TV デバイスまたは仮想デバイス
2. セットアップする
この Codelab のテーマ設定と基本設定を含むコードを取得するには、次のいずれかを行います。
- この GitHub リポジトリからコードのクローンを作成します。
$ git clone https://github.com/android/tv-codelabs.git
main
ブランチにはスターター コードが含まれ、solution
ブランチには解答コードが含まれます。
- スターター コードを含む
main.zip
ファイルと、解答コードを含むsolution.zip
ファイルをダウンロードします。
コードがダウンロードされたところで、Android Studio で IntroductionToComposeForTV プロジェクト フォルダを開きます。これで準備が整いました。
3.カタログ ブラウザ画面を実装する
カタログ ブラウザ画面では、映画のカタログをブラウジングできます。カタログ ブラウザをコンポーズ可能な関数として実装します。コンポーズ可能な関数 CatalogBrowser
は CatalogBrowser.kt
ファイルにあります。このコンポーズ可能な関数で、カタログ ブラウザ画面を実装します。
スターター コードには、CatalogBrowserViewModel
クラスと呼ばれる ViewModel があります。このクラスには、映画のコンテンツを記述する Movie
オブジェクトを取得するための属性とメソッドがいくつかあります。取得した Movie
オブジェクトを使用してカタログ ブラウザを実装します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
カテゴリ名を表示する
Category
リストのフローである catalogBrowserViewModel.categoryList
属性を使用して、カテゴリのリストにアクセスできます。フローは、collectAsStateWithLifecycle
メソッドを呼び出すことにより Compose State
オブジェクトとして収集されます。Category
オブジェクトには name
属性があります。この属性は、カテゴリ名を表す String
値です。
カテゴリ名を表示する手順は次のとおりです。
- Android Studio でスターター コードの
CatalogBrowser.kt
ファイルを開き、コンポーズ可能な関数LazyColumn
をコンポーズ可能な関数CatalogBrowser
に追加します。 catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
メソッドを呼び出して、フローをState
オブジェクトとして収集します。categoryList
は、前の手順で作成したState
オブジェクトの委任プロパティとして宣言します。categoryList
変数をパラメータとしてitems
関数を呼び出します。- ラムダの引数として渡されるパラメータとしてカテゴリ名を指定し、コンポーズ可能な関数
Text
を呼び出します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
}
}
}
各カテゴリのコンテンツ リストを表示する
Category
オブジェクトには、movieList
という属性があります。この属性は、カテゴリに属する映画を表す Movie
オブジェクトのリストです。
各カテゴリのコンテンツ リストを表示する手順は次のとおりです。
- コンポーズ可能な関数
LazyRow
を追加してラムダを渡します。 - ラムダ内で、
category
を使用してitems
関数を呼び出します。movieList
属性値を取得してラムダを渡します。 items
関数に渡されたラムダ内で、Movie
オブジェクトを指定してコンポーズ可能な関数MovieCard
を呼び出します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
LazyRow {
items(category.movieList) {movie ->
MovieCard(movie = movie)
}
}
}
}
}
省略可: レイアウトを調整する
- カテゴリ間の間隔を設定するには、
verticalArrangement
パラメータを使用してArrangement
オブジェクトをコンポーズ可能な関数LazyColumn
に渡します。Arrangement
オブジェクトは、Arrangement#spacedBy
メソッドを呼び出すことにより作成されます。 - 映画カードの間隔を設定するには、
horizontalArrangement
パラメータを使用してArrangement
オブジェクトをコンポーズ可能な関数LazyRow
に渡します。 - 列にインデントを設定するには、
contentPadding
パラメータでPaddingValue
オブジェクトを渡します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie)
}
}
}
}
}
4. 詳細画面を実装する
詳細画面には、選択した映画の詳細が表示されます。Details.kt
ファイル内に、コンポーズ可能な関数 Details
があります。この関数にコードを追加して、詳細画面を実装します。
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
ムービーのタイトル、スタジオ名、説明を表示する
Movie
オブジェクトには、映画のメタデータとして次の 3 つの文字列属性があります。
title
。映画のタイトル。studio
。映画を制作したスタジオの名前。description
。映画の簡単な要約。
このメタデータを詳細画面に表示する手順は次のとおりです。
- コンポーズ可能な関数
Column
を追加し、Modifier.padding
メソッドで作成したModifier
オブジェクトを使用して、縦方向 32 dp と横方向 48 dp のクリアランスを列に設定します。 - 映画のタイトルを表示するには、コンポーズ可能な関数
Text
を追加します。 - スタジオ名を表示するには、コンポーズ可能な関数
Text
を追加します。 - 映画の説明を表示するには、コンポーズ可能な関数
Text
を追加します。
Details.kt
@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.description)
}
}
コンポーズ可能な関数 Details
のパラメータで指定された Modifier
オブジェクトは、次のタスクで使用します。
特定の Movie
オブジェクトに関連付けられている背景画像を表示する
Movie
オブジェクトには backgroundImageUrl
属性があり、そのオブジェクトで記述されている映画の背景画像の場所が示されます。
特定の映画の背景画像を表示する手順は次のとおりです。
- コンポーズ可能な関数
Box
をコンポーズ可能な関数Column
のラッパーとして追加し、modifier
オブジェクトをコンポーズ可能な関数Details
を通じて渡します。 - コンポーズ可能な関数
Box
内で、modifier
オブジェクトのfillMaxSize
メソッドを呼び出して、コンポーズ可能な関数Box
がコンポーズ可能な関数Details
に割り当てることのできる最大サイズを満たすようにします。 - 次のパラメータを含むコンポーズ可能な関数
AsyncImage
をコンポーズ可能な関数Box
に追加します。
- 指定された
Movie
オブジェクトのbackgroundImageUrl
属性の値をmodel
パラメータに設定します。 null
をcontentDescription
パラメータに渡します。
ContentScale.Crop
オブジェクトをcontentScale
パラメータに渡します。さまざまなContentScale
オプションについては、コンテンツ スケールをご覧ください。Modifier.fillMaxSize
メソッドの戻り値をmodifier
パラメータに渡します。
Details.kt
@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 {
Text(
text = movie.title,
)
Text(
text = movie.studio,
)
Text(text = movie.description)
}
}
}
テーマ設定の一貫性を保つために MaterialTheme
オブジェクトを参照する
MaterialTheme
オブジェクトには、Typography
クラスや ColorScheme
クラスなどにある現在のテーマ値を参照する関数が含まれています。
テーマ設定の一貫性を保つために MaterialTheme
オブジェクトを参照する手順は次のとおりです。
MaterialTheme.typography.displayMedium
プロパティに、映画のタイトルのテキスト スタイルを設定します。MaterialTheme.typography.bodySmall
プロパティに、2 番目のコンポーズ可能な関数Text
のテキスト スタイルを設定します。Modifier.background
メソッドを使用して、MaterialTheme.colorScheme.background
プロパティに、コンポーズ可能な関数Column
の背景色を設定します。
Details.kt
@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
.background(MaterialTheme.colorScheme.background),
) {
Text(
text = movie.title,
style = MaterialTheme.typography.displayMedium,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.bodySmall,
)
Text(text = movie.description)
}
}
}
省略可: レイアウトを調整する
コンポーズ可能な関数 Details
のレイアウトを調整する手順は次のとおりです。
fillMaxSize
修飾子により、使用可能なスペース全体を使用するようにコンポーズ可能な関数Box
を設定します。background
修飾子でコンポーズ可能な関数Box
の背景を設定し、MaterialTheme.colorScheme.background
値とColor.Transparent
を含むColor
オブジェクトのリストでBrush.linearGradient
関数を呼び出すことにより作成される線形グラデーションで背景を塗りつぶします。padding
修飾子を使用して、コンポーズ可能な関数Column
の周囲に横方向48.dp
と縦方向24.dp
のクリアランスを設定します。0.5f
値を指定してModifier.width
関数を呼び出すことにより作成されるwidth
修飾子を使用して、コンポーズ可能な関数Column
の幅を設定します。Spacer
を使用して、2 番目のコンポーズ可能な関数Text
と 3 番目のコンポーズ可能な関数Text
の間に8.dp
のスペースを追加します。コンポーズ可能な関数Spacer
の高さは、Modifier.height
関数で作成されたheight
修飾子で指定します。
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.background(
Brush.linearGradient(
listOf(
MaterialTheme.colorScheme.background,
Color.Transparent
)
)
)
.fillMaxSize()
) {
Column(
modifier = Modifier
.padding(horizontal = 48.dp, vertical = 24.dp)
.fillMaxWidth(0.5f)
) {
Text(
text = movie.title,
style = MaterialTheme.typography.displayMedium,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.bodySmall,
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = movie.description,
)
}
}
}
}
5. 画面間のナビゲーションを追加する
これで、カタログ ブラウザと詳細画面が表示されます。ユーザーがカタログ ブラウザ画面でコンテンツを選択した後は、詳細画面に遷移する必要があります。そのためには、clickable
修飾子を使用して、event
リスナーをコンポーズ可能な関数 MovieCard
に追加します。十字キーの中央ボタンが押されると、コンポーズ可能な関数 MovieCard
に関連付けられた映画オブジェクトを引数として CatalogBrowserViewModel#showDetails
メソッドが呼び出されます。
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
ファイルを開きます。onClick
パラメータを指定して、ラムダ関数をコンポーズ可能な関数MovieCard
に渡します。- コンポーズ可能な関数
MovieCard
に関連付けられた映画オブジェクトを指定して、onMovieSelected
コールバックを呼び出します。
CatalogBrowser.kt
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
6. カタログ ブラウザの画面にカルーセルを追加して、注目のコンテンツをハイライト表示する
カルーセルは、指定された時間が経過するとスライドを自動的に更新する、適応性の高い UI コンポーネントです。通常は、注目のコンテンツをハイライト表示するために使用します。
カルーセルをカタログ ブラウザ画面に追加して、注目のコンテンツ リストで映画をハイライトする手順は次のとおりです。
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
ファイルを開きます。item
関数を呼び出して、アイテムをコンポーズ可能な関数LazyColumn
に追加します。item
関数に渡されたラムダ内の委任プロパティとしてfeaturedMovieList
を宣言し、catalogBrowserViewModel.featuredMovieList
属性から収集されるState
オブジェクトを委任に設定します。item
関数内でコンポーズ可能な関数Carousel
を呼び出し、次のパラメータを渡します。
slideCount
パラメータを介したfeaturedMovieList
変数のサイズ。Modifier.fillMaxWidth
メソッドとModifier.height
メソッドでカルーセル サイズを指定するためのModifier
オブジェクト。コンポーズ可能な関数Carousel
は、376.dp
値をModifier.height
メソッドに渡して、376 dp の高さを使用します。- 表示されるカルーセル アイテムのインデックスを示す整数値で呼び出されるラムダ。
featuredMovieList
変数と指定されたインデックス値からMovie
オブジェクトを取得します。- コンポーズ可能な関数
Box
をコンポーズ可能な関数Carousel
に追加します。 - 映画のタイトルを表示するには、コンポーズ可能な関数
Text
をコンポーズ可能な関数Box
に追加します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp)
) { indexOfCarouselSlide ->
val featuredMovie =
featuredMovieList[indexOfCarouselSlide]
Box {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
背景画像を表示する
コンポーズ可能な関数 Box
は、あるコンポーネントを別のコンポーネントの上に配置します。詳しくは、レイアウトの基本をご覧ください。
背景画像を表示する手順は次のとおりです。
- コンポーズ可能な関数
AsyncImage
を呼び出して、コンポーズ可能な関数Text
の前で、Movie
オブジェクトに関連付けられた背景画像を読み込みます。 - 視認性を高めるため、コンポーズ可能な関数
Text
の位置とテキスト スタイルを更新します。 - レイアウト シフトを避けるため、プレースホルダにコンポーズ可能な関数
AsyncImage
を設定します。スターター コードには、R.drawable.placeholder
で参照できるドローアブルとしてプレースホルダがあります。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
Box{
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)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
詳細画面への画面遷移を追加する
Button
をカルーセルに追加すると、ユーザーがボタンをクリックして詳細画面への画面遷移をトリガーできるようになります。
詳細画面に表示されるカルーセルでユーザーが映画の詳細を確認できるようにする手順は次のとおりです。
Carousel
コンポーザブル内のBox
コンポーザブルで、コンポーズ可能な関数Column
を呼び出します。Carousel
のText
コンポーザブルをコンポーズ可能な関数Column
に移動します。- コンポーズ可能な関数
Column
内のコンポーズ可能な関数Text
の後で、コンポーズ可能な関数Button
を呼び出します。 R.string.show_details
で呼び出されたstringResource
関数の戻り値を使用して、コンポーズ可能な関数Button
内でコンポーズ可能な関数Text
を呼び出します。- コンポーズ可能な関数
Button
のonClick
パラメータに渡されたラムダ内でfeaturedMovie
変数を指定して、onMovieSelected
関数を呼び出します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
Box {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
Column {
Text(text = featuredMovie.title)
Button(onClick = { onMovieSelected(featuredMovie) }) {
Text(text = stringResource(id = R.string.show_details))
}
}
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
省略可: レイアウトを調整する
カルーセルのレイアウトを調整する手順は次のとおりです。
- コンポーズ可能な関数
Carousel
内で、backgroundColor
値にMaterialTheme.colorScheme.background
値を代入します。 - コンポーズ可能な関数
Column
をBox
コンポーザブルでラップします。 Alignment.BottomStart
値をBox
コンポーネントのcontentAlignment
パラメータに渡します。fillMaxSize
修飾子をコンポーズ可能な関数Box
の修飾子パラメータに渡します。fillMaxSize
修飾子はModifier.fillMaxSize()
関数で作成されます。Box
コンポーザブルに渡されたfillMaxSize
修飾子に対してdrawBehind()
メソッドを呼び出します。drawBehind
修飾子に渡されたラムダ内で、Brush
オブジェクトを使用してbrush
値を代入します。このオブジェクトは、2 つのColor
オブジェクトのリストを指定してBrush.linearGradient
関数を呼び出すことにより作成されます。このリストは、backgroundColor
値とColor.Transparent
値を指定してlistOf
関数を呼び出すことにより作成されます。drawBehind
修飾子に渡されたラムダ内のbrush
オブジェクトを指定してdrawRect
を呼び出し、背景画像の上にスクリムレイヤを作成します。padding
修飾子を使用して、コンポーズ可能な関数Column
のパディングを指定します。この修飾子は、20.dp
値を指定してModifier.padding
を呼び出すことにより作成されます。- コンポーズ可能な関数
Column
内で、Text
コンポーザブルとButton
コンポーザブルの間に、20.dp
の値を含むコンポーズ可能な関数Spacer
を追加します。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(32.dp),
contentPadding = PaddingValues(horizontal = 58.dp, vertical = 36.dp)
) {
item {
val featuredMovieList by
catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
itemCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
val backgroundColor = MaterialTheme.colorScheme.background
Box {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
Box(
contentAlignment = Alignment.BottomStart,
modifier = Modifier
.fillMaxSize()
.drawBehind {
val brush = Brush.horizontalGradient(
listOf(backgroundColor, Color.Transparent)
)
drawRect(brush)
}
) {
Column(
modifier = Modifier.padding(20.dp)
) {
Text(
text = featuredMovie.title,
style = MaterialTheme.typography.displaySmall
)
Spacer(modifier = Modifier.height(28.dp))
Button(onClick = { onMovieSelected(featuredMovie) }) {
Text(text = stringResource(id = R.string.show_details))
}
}
}
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.height(200.dp)
) {
items(category.movieList) { movie ->
MovieCard(
movie,
onClick = {
onMovieSelected(it)
}
)
}
}
}
}
}
7. 解答コードを取得する
この Codelab の解答コードをダウンロードするには、次のいずれかを行います。
- 次のボタンをクリックして ZIP ファイルとしてダウンロードし、Android Studio で開きます。
- Git を使用して取得します。
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
8. これで完了です。
お疲れさまでした。Compose for TV の基本を習得しました。
- LazyColumn と LazyLow を組み合わせてコンテンツ リストを表示する画面の実装方法。
- コンテンツの詳細を表示する基本的な画面の実装。
- 2 つの画面間の画面遷移を追加する方法。