1. 事前準備
Compose for TV 是最新的 UI 架構,用於開發在 Android TV 上執行的應用程式。此架構可享有適用於 TV 應用程式的 Jetpack Compose 所有優勢,更輕鬆地為應用程式建構美觀且實用的使用者介面。Compose for TV 的一些特定優點如下:
- 高度彈性。Compose 可用於建立任何類型的 UI,從簡單的版面配置到複雜的動畫都沒問題。元件可立即使用,但也可以配合應用程式需求自訂及設定樣式。
- 簡化及加速開發作業。Compose 與現有程式碼相容,且可讓開發人員用較少的程式碼建構應用程式。
- 操作直覺:Compose 採用宣告式語法,讓使用者以符合直覺的方式變更使用者介面,以及偵錯、解讀和檢查程式碼。
電視應用程式的常見用途是使用媒體。使用者瀏覽內容目錄,然後選取要觀看的內容。內容可以是電影、電視節目或 Podcast。使用者選取內容後,可能會想查看更多內容資訊,例如簡短說明、播放長度和創作者名稱。在本程式碼研究室中,您可以學到如何使用 Compose for TV 實作目錄瀏覽器畫面和詳細資料畫面。
必要條件
- 具備 Kotlin 語法經驗 (包括 lambda)。
- 具備 Compose 的基本經驗。如果您不熟悉 Compose,請先完成「Jetpack Compose 基本概念」程式碼研究室。
- 具備可組合函式和修飾符的基本知識。
建構內容
- 包含目錄瀏覽器畫面和詳細資料畫面的影片播放器應用程式。
- 目錄瀏覽器畫面,顯示使用者可選擇的影片清單。如下圖所示:
- 詳細資料畫面,顯示所選影片的中繼資料,例如標題、說明和長度。如下圖所示:
需求條件
- 最新版 Android Studio
2. 做好準備
如要取得含有本程式碼研究室的主題設定和基本設定程式碼,請執行下列任一操作:
- 複製這個 GitHub 存放區中的程式碼:
$ git clone https://github.com/android/tv-codelabs.git
main
分支版本含有範例程式碼,solution
分支版本則包含解決方案程式碼。
- 下載含有範例程式碼的
main.zip
檔案,以及包含解決方案程式碼的solution.zip
檔案。
下載程式碼後,請在 Android Studio 中開啟「IntroductionToComposeForTV」專案資料夾。您已完成開始本研究室的準備。
3. 實作目錄瀏覽器畫面
目錄瀏覽器畫面可讓使用者瀏覽電影目錄。您要將目錄瀏覽器實作為 Composable
函式。您可以在 CatalogBrowser.kt
檔案中找到 CatalogBrowser
Composable
函式。您會在這個 Composable
函式中實作目錄瀏覽器畫面。
範例程式碼含有稱為 CatalogBrowserViewModel
類別的 ViewModel,其中包含幾種屬性和方法,用於擷取描述電影內容的 Movie
物件。您要使用擷取的 Movie
物件實作目錄瀏覽器。
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 = {}
) {
}
顯示類別名稱
您可以使用 catalogBrowserViewModel.categoryList
屬性 (即 Category
清單的資料流) 存取類別清單。資料流會透過呼叫其 collectAsState
方法,將類別收集為 Compose State
物件。Category
物件包含 name
屬性,是代表類別名稱的 String
值。
如要顯示類別名稱,請按照下列步驟操作:
- 在 Android Studio 中開啟範例程式碼的
CatalogBrowser.kt
檔案,然後將TvLazyColumn
Composable
函式新增至CatalogBrowser
Composable
函式。 - 呼叫
catalogBrowserViewModel.categoryList.collectAsState()
方法,將資料流收集為State
物件。 - 將
categoryList
宣告為您在上一個步驟中所建立State
物件的委派屬性。 - 使用
categoryList
變數做為參數來呼叫items
函式。 - 使用類別名稱做為參數來呼叫
Text
Composable
函式,該參數會做為 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)
}
}
}
顯示每個類別的內容清單
Category
物件還有另一個名為 movieList
的屬性。該屬性是 Movie
物件清單,代表屬於該類別的電影。
如要顯示各個類別的內容清單,請按照下列步驟操作:
- 新增
TvLazyRow
Composable
函式,然後將 lambda 傳遞至該函式。 - 在 lambda 中,使用
category
.movieList
屬性值呼叫items
函式,然後將 lambda 傳遞至該函式。 - 在傳遞至
items
函式的 lambda 中,使用Movie
物件呼叫MovieCard
Composable
函式。
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)
}
}
}
}
}
選用:調整版面配置
- 如要設定類別之間的間距,請使用
verticalArrangement
參數將Arrangement
物件傳遞至TvLazyColumn
Composable
函式。請透過呼叫Arrangement#spacedBy
方法來建立Arrangement
物件。 - 如要設定電影資訊卡之間的間距,請使用
horizontalArrangement
參數將Arrangement
物件傳遞至TvLazyRow
Composable
函式。 - 若要為資料欄設定縮排,請使用
contentPadding
參數傳遞PaddingValue
物件。
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. 實作詳細資料畫面
詳細資料畫面會顯示所選電影的詳細資料。Details.kt
檔案中有 Details
Composable
函式。請將程式碼新增至這個函式,以實作詳細資料畫面。
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) {
}
顯示電影名稱、電影公司名稱和說明
Movie
物件包含下列三個字串屬性,做為電影的中繼資料:
title
:電影名稱。studio
:製作電影的公司名稱。description
:電影的簡短摘要。
如要在詳細資料畫面上顯示這項中繼資料,請按照下列步驟操作:
- 新增
Column
Composable
函式,然後使用由Modifier.padding
方法建立的Modifier
物件,將資料欄周圍的垂直間距設為 32 dp,水平間距設為 48 dp。 - 新增
Text
Composable
函式以顯示電影標題。 - 新增
Text
Composable
函式以顯示電影公司名稱。 - 新增
Text
Composable
函式以顯示電影說明。
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)
}
}
在 Details
Composable
函式的參數中指定的 Modifier
物件,會在下一個工作中用到。
顯示與指定 Movie
物件相關聯的背景圖片
Movie
物件包含 backgroundImageUrl
屬性,表示物件所描述電影的背景圖片位置。
如要顯示特定電影的背景圖片,請按照下列步驟操作:
- 新增
Box
Composable
函式做為Column
Composable
函式的包裝函式,其中modifier
物件是透過Details
Composable
函式傳遞。 - 在
Box
Composable
函式中,呼叫modifier
物件的fillMaxSize
方法,讓Box
Composable
函式填入可分配至Details
Composable
函式的最大值。 - 將含有下列參數的
AsyncImage
Composable
函式新增至Box
Composable
函式:
- 將指定
Movie
物件的backgroundImageUrl
屬性值設為model
參數。 - 將
null
傳遞至contentDescription
參數。
- 將
ContentScale.Crop
物件傳遞至contentScale
參數。如要查看不同的ContentScale
選項,請參閱「內容縮放」。 - 將
Modifier.fillMaxSize
方法的傳回值傳遞至modifier
參數。 - 使用呼叫
Modifier.padding
方法建立的Modifier
物件,將資料欄的垂直間距設為 32 dp,水平間距設為 48 dp。
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,
)
}
}
}
參照 MaterialTheme
物件,讓主題設定保持一致
MaterialTheme
物件含有用於參照目前主題值的函式,例如 Typography
和 [ColorScheme
][ColorScheme] 類別中的值。
如要參照 MaterialTheme
物件以讓主題設定保持一致,請按照下列步驟操作:
- 將
MaterialTheme.typography.headlineLarge
屬性設為電影標題的文字樣式。 - 將
MaterialTheme.typography.headlineMedium
屬性設為其他兩個Text
Composable
函式的文字樣式。 - 使用
Modifier.background
方法,將MaterialTheme.colorScheme.background
屬性設為Column
Composable
函式的背景顏色。
[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. 在畫面之間新增導覽
現在您已擁有目錄瀏覽器畫面和詳細資料畫面。使用者在目錄瀏覽器畫面中選取內容後,該畫面就必須轉換為詳細資料畫面。為此,請使用 clickable
修飾符在 MovieCard
Composable
函式中加入 event
事件監聽器。當使用者按下方向鍵中間的按鈕時,請使用與 MovieCard
Composable
函式相關聯的電影物件做為引數來呼叫 CatalogBrowserViewModel#showDetails
方法。
- 開啟
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
檔案。 - 使用
onClick
參數將 lambda 函式傳遞至MovieCard
Composable
函式。 - 使用與
MovieCard
Composable
函式相關聯的電影物件呼叫onMovieSelected
回呼。
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. 將輪轉介面加入目錄瀏覽器畫面,藉此凸顯精選內容
輪轉介面是一種常見的自動調整式 UI 元件,會在指定的時間長度過後自動更新介面的投影片,通常用來凸顯精選內容。
如要在目錄瀏覽器畫面中新增輪轉介面,藉此凸顯精選內容清單中的電影,請按照下列步驟操作:
- 開啟
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
檔案。 - 呼叫
item
函式,將項目新增至TvLazyColumn
Composable
函式。 - 在傳遞至
item
函式的 lambda 中宣告featuredMovieList
做為委派屬性,然後將State
物件 (從catalogBrowserViewModel.featuredMovieList
屬性收集該物件) 設為委派項目。 - 在
item
函式中呼叫Carousel
Composable
函式,然後傳入下列參數:
- 透過
slideCount
參數設定featuredMovieList
變數的大小。 Modifier
物件,用於透過Modifier.fillMaxWidth
和Modifier.height
方法指定輪轉介面的大小。Carousel
Composable
函式會藉由將376.dp
值傳遞至Modifier.height
方法,將高度設為 376 dp。- 透過整數值呼叫的 lambda,代表可見輪轉介面項目的索引。
- 從
featuredMovieList
變數和指定的索引值擷取Movie
物件。 - 在
Carousel
Composable
函式中加入CarouselSlide
Composable
函式。 - 在
CarouselSlide
Composable
函式中加入Text
Composable
函式,以顯示電影標題。
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) })
}
}
}
}
}
顯示背景圖片
CarouselSlide
Composable
函式可接受另一個 lambda,以指定 CarouselSlide
Composable
函式的背景顯示方式。
如要顯示背景圖片,請按照下列步驟操作:
- 使用
background
參數將 lambda 傳送至CarouselSlide
Composable
函式。 - 呼叫
AsyncImage
Composable
函式,將與Movie
物件相關聯的背景圖片載入到CarouselSlide
Composable
函式的背景。 - 更新
CarouselSlide
Composable
函式中Text
Composable
函式的位置和文字樣式,讓畫面更加清楚。 - 將預留位置設為
AsyncImage
Composable
函式,以免版面配置位移。範例程式碼含有預留位置做為可繪項目,您可以使用R.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) })
}
}
}
}
}
在詳細資料畫面中新增畫面轉場
您可以讓使用者按一下 CarouselSlide
Composable
函式。
如要讓使用者在詳細資料畫面上,查看可見輪轉介面項目中的電影詳細資料,請按照下列步驟操作:
- 透過
modifier
參數,將Modifier.clickable
方法的傳回值傳遞至CarouselSlide
Composable
函式。 - 在傳遞至
Modifier.clickable
方法的 lambda 中,使用可見的CarouselSlide
Composable
函式的Movie
物件呼叫onMovieSelected
函式。
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. 取得解決方案程式碼
如要下載本程式碼研究室的解決方案程式碼,請執行下列任一操作:
- 點選下方按鈕即可將其下載為 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 的基本概念:
- 如何透過結合 TvLazyColumn 和 TvLazyLOW,實作顯示內容清單的螢幕畫面。
- 實作顯示內容詳細資料的基本畫面。
- 如何在兩個畫面之間新增畫面轉場。