1. 开始之前
Compose for TV 是用于开发在 Android TV 上运行的应用的最新 UI 框架。它为 TV 应用解锁了 Jetpack Compose 的所有优势,让您的应用更轻松地构建美观且功能强大的 UI。Compose for TV 的一些特定优势包括:
- 灵活性。Compose 可用于创建任何类型的 UI,从简单的布局到复杂的动画。组件开箱即用,但也可以根据您的应用需求进行自定义和样式设置。
- 简化和加速开发。Compose 与现有代码兼容,并允许开发者用更少的代码构建应用。
- 直观性:Compose 使用声明式语法,使您能够直观地更改 UI,并调试、理解和审查代码。
电视应用的一个常见用例是媒体消费。用户浏览内容目录并选择他们想要观看的内容。内容可以是电影、电视节目或播客。用户选择内容后,他们可能希望查看更多相关信息,例如简短描述、播放时长和创作者姓名。在本 Codelab 中,您将学习如何使用 Compose for TV 实现目录浏览器屏幕和详细信息屏幕。
前提条件
- 熟悉 Kotlin 语法,包括 lambda 表达式。
- 具有 Compose 的基本经验。如果您不熟悉 Compose,请完成Jetpack Compose 基础知识 Codelab。
- 具备可组合项和修饰符的基本知识。
- 以下任一设备,用于运行示例应用:
- Android TV 设备
- 具有 TV 设备定义类别中配置文件的 Android 虚拟设备
您将构建什么
- 一个包含目录浏览器屏幕和详细信息屏幕的视频播放器应用。
- 一个目录浏览器屏幕,显示供用户选择的视频列表。它看起来像下图:
- 一个详细信息屏幕,显示所选视频的元数据,如标题、描述和时长。它看起来像下图:
您需要什么
- 最新版本的Android Studio
- Android TV 设备或 TV 设备类别中的虚拟设备
2. 设置
要获取包含本 Codelab 主题和基本设置的代码,请执行以下操作之一:
- 从此 GitHub 仓库克隆代码。
$ git clone https://github.com/android/tv-codelabs.git
main
分支包含起始代码,solution
分支包含解决方案代码。
- 下载包含起始代码的
main.zip
文件,以及包含解决方案代码的solution.zip
文件。
现在您已经下载了代码,请在 Android Studio 中打开 IntroductionToComposeForTV 项目文件夹。您现在可以开始了。
3. 实现目录浏览器屏幕
目录浏览器屏幕允许用户浏览电影目录。您将目录浏览器实现为可组合函数。您可以在 CatalogBrowser.kt
文件中找到 CatalogBrowser
可组合函数。您将在该可组合函数中实现目录浏览器屏幕。
起始代码中有一个名为 CatalogBrowserViewModel
的 ViewModel 类,它具有多个属性和方法来检索描述电影内容的 Movie
对象。您将使用检索到的 Movie
对象实现目录浏览器。
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
显示类别名称
您可以通过 catalogBrowserViewModel.categoryList
属性访问类别列表,该属性是 Category
列表的流。通过调用其 collectAsStateWithLifecycle
方法,该流被收集为 Compose State
对象。Category
对象具有 name
属性,该属性是一个表示类别名称的 String
值。
要显示类别名称,请按照以下步骤操作:
- 在 Android Studio 中,打开起始代码的
CatalogBrowser.kt
文件,然后将一个LazyColumn
可组合函数添加到CatalogBrowser
可组合函数中。 - 调用
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
方法将流收集为State
对象。 - 将
categoryList
声明为您在上一步中创建的State
对象的委托属性。 - 使用
categoryList
变量作为参数调用items
函数。 - 使用类别名称作为 lambda 的参数调用
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
可组合函数,然后向其传递一个 lambda 表达式。 - 在该 lambda 表达式中,使用
category
.movieList
属性值调用items
函数,然后向其传递一个 lambda 表达式。 - 在传递给
items
函数的 lambda 表达式中,使用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)
}
}
}
}
}
可选:调整布局
- 要设置类别之间的间距,请将
Arrangement
对象以及verticalArrangement
参数传递给LazyColumn
可组合函数。通过调用Arrangement#spacedBy
方法来创建Arrangement
对象。 - 要设置电影卡之间的间距,请将
Arrangement
对象以及horizontalArrangement
参数传递给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
对象具有以下三个字符串属性作为电影的元数据:
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
可组合函数的包装器,并使用通过Details
可组合函数传递的modifier
对象。 - 在
Box
可组合函数中,调用modifier
对象的fillMaxSize
方法,使Box
可组合函数填充分配给Details
可组合函数的最大尺寸。 - 向
Box
可组合函数添加一个AsyncImage
可组合函数,并使用以下参数:
- 将给定
Movie
对象的backgroundImageUrl
属性值设置为model
参数。 - 向
contentDescription
参数传递null
。
- 向
contentScale
参数传递ContentScale.Crop
对象。要查看不同的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
属性设置为第二个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
可组合函数的布局,请按照以下步骤操作:
- 将
Box
可组合函数设置为使用所有可用空间,使用fillMaxSize
修饰符 - 使用
background
修饰符设置Box
可组合函数的背景,以线性渐变填充背景,该线性渐变通过调用Brush.linearGradient
函数并传递包含MaterialTheme.colorScheme.background
值和Color.Transparent
的Color
对象列表来创建 - 使用
padding
修饰符在Column
可组合函数周围设置48.dp
水平间距和24.dp
垂直间距 - 使用通过调用
Modifier.width
函数并传递0.5f
值来创建的width
修饰符设置Column
可组合函数的宽度 - 在第二个
Text
可组合函数和第三个Text
可组合函数之间添加8.dp
的间距,使用Spacer
。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
修饰符为 MovieCard
可组合函数添加一个 event
监听器。当方向键的中心按钮被按下时,将调用 CatalogBrowserViewModel#showDetails
方法,并将与 MovieCard
可组合函数关联的电影对象作为参数。
- 打开
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
文件。 - 将 lambda 函数与
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
函数的 lambda 表达式中,将featuredMovieList
声明为委托属性,然后设置要委托的State
对象,该对象是从catalogBrowserViewModel.featuredMovieList
属性收集的。 - 在
item
函数内部调用Carousel
可组合函数,然后传入以下参数:
- 通过
slideCount
参数传递featuredMovieList
变量的大小。 - 一个
Modifier
对象,用于使用Modifier.fillMaxWidth
和Modifier.height
方法指定轮播大小。Carousel
可组合函数通过向Modifier.height
方法传递376.dp
值来使用 376 dp 的高度。 - 一个 lambda 表达式,带有一个整数值,表示可见轮播项的索引。
- 从
featuredMovieList
变量和给定索引值中检索Movie
对象。 - 向
Carousel
可组合函数添加一个Box
可组合函数。 - 向
Box
可组合函数添加一个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,
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
可组合函数将一个组件放置在另一个组件之上。有关详细信息,请参阅布局基础知识。
要显示背景图片,请按照以下步骤操作:
- 在
Text
可组合函数之前,调用AsyncImage
可组合函数以加载与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
可组合函数 - 在
Button
可组合函数中,使用调用stringResource
函数并传入R.string.show_details
获得的返回值来调用Text
可组合函数。 - 在传入给
Button
可组合函数的onClick
参数的 lambda 表达式中,使用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
值 - 用
Box
可组合项包装Column
可组合函数 - 将
Alignment.BottomStart
值传递给Box
组件的contentAlignment
参数。 - 将
fillMaxSize
修饰符传递给Box
可组合函数的 modifier 参数。fillMaxSize
修饰符是使用Modifier.fillMaxSize()
函数创建的。 - 在传递给
Box
可组合函数的fillMaxSize
修饰符之上调用drawBehind()
方法 - 在传递给
drawBehind
修饰符的 lambda 表达式中,将brush
值赋给通过调用Brush.linearGradient
函数并传递两个Color
对象的列表来创建的Brush
对象。该列表是通过调用listOf
函数并传递backgroundColor
值和Color.Transparent
值来创建的。 - 在传递给
drawBehind
修饰符的 lambda 表达式中调用drawRect
并传入brush
对象,以便在背景图像上方创建一个蒙版层 - 使用通过调用
Modifier.padding
并传入20.dp
值来创建的padding
修饰符指定Column
可组合函数的内边距。 - 在
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 和 LazyRow 实现显示内容列表的屏幕。
- 显示内容详细信息的基本屏幕实现。
- 如何在两个屏幕之间添加屏幕转换。