1. 准备工作
Compose for TV 是用于开发在 Android TV 上运行的应用的最新应用框架。它融合了适用于 TV 应用的 Jetpack Compose 的所有优势,可让您更轻松地为应用构建功能强大且美观出众的界面。Compose for TV 的一些具体优势包括:
- 灵活性。Compose 可用于创建任何类型的界面,从简单的布局到复杂的动画。组件是可直接使用的,但也可以进行自定义和样式设置,以满足您的应用的需求。
- 简化并加速开发。Compose 与现有代码兼容,让开发者可以使用更少的代码构建应用。
- 直观:Compose 使用声明式语法,直观地更改界面以及调试、理解和审核代码。
TV 应用的常见应用场景是媒体使用。用户浏览内容目录并选择想要观看的内容。这些内容可以是电影、电视节目或播客。用户选择一段内容后,他们可能需要查看更多信息,例如简短说明、播放时长和创作者的姓名。在本 Codelab 中,您将学习如何使用 Compose for TV 实现目录浏览器页面和详情页面。
前提条件
- 有使用 Kotlin 语法(包括 lambda)的经验。
- 有使用 Compose 的基本经验。如果您不熟悉 Compose,请先完成 Jetpack Compose 基础知识 Codelab。
- 具备可组合函数和修饰符方面的基础知识。
- 用于运行示例应用的以下任意设备:
- Android TV 设备
- Android 虚拟设备,包含属于 TV 设备定义类别的配置文件。
构建内容
- 您将构建一个视频播放器应用,其中包含一个目录浏览器页面和一个详情页面。
- 目录浏览器页面将显示一系列视频供用户选择,如下图所示:
- 详情页面会显示所选视频的元数据,例如标题、说明和时长,如下图所示:
所需条件
- 最新版本的 Android Studio
- 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.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
列表的数据流。该数据流是以 ComposeState
对象的形式收集的(通过调用其 collectAsStateWithLifecycle
方法)。Category
对象具有 name
属性,该属性是表示类别名称的 String
值。
如需显示类别名称,请按以下步骤操作:
- 在 Android Studio 中,打开起始代码的
CatalogBrowser.kt
文件,然后将LazyColumn
可组合函数添加到CatalogBrowser
可组合函数。 - 调用
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
方法以State
对象的形式收集数据流。 - 将
categoryList
声明为您在上一步中创建的State
对象的委托属性。 - 使用
categoryList
变量作为参数来调用items
函数。 - 使用类别名称作为参数调用
Text
可组合函数,该参数将作为 lambda 的参数传递。
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
可组合函数。 - 如需为列设置缩进,请将
PaddingValue
对象作为contentPadding
参数传递。
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
可组合函数的最大大小。 - 将包含以下参数的
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
属性设置为第二个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
- 使用
width
修饰符设置Column
可组合函数的宽度,该修饰符是通过调用Modifier.width
函数(使用0.5f
值)创建的 - 使用
Spacer
在第二个Text
可组合函数和第三个Text
可组合项之间添加8.dp
的空间。使用通过Modifier.height
函数创建的height
修饰符指定Spacer
可组合函数的高度
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
文件。 - 使用
onClick
参数将 lambda 函数传递给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. 在目录浏览器页面中添加轮播界面,以突出显示精选内容
轮播界面是一个自适应调整的界面组件,会在指定时长后自动更新其幻灯片,通常用于突出显示精选内容。
如需将轮播界面添加到目录浏览器页面以突出显示精选内容列表中的电影,请按以下步骤操作:
- 打开
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
文件。 - 调用
item
函数,为LazyColumn
可组合函数添加项。 - 在传递给
item
函数的 lambda 中将featuredMovieList
声明为委托属性,然后将State
对象设置为委托对象,后者是从catalogBrowserViewModel.featuredMovieList
属性收集的。 - 在
item
函数中调用Carousel
可组合函数,然后传入以下参数:
- 通过
slideCount
参数设置的featuredMovieList
变量的大小。 - 使用
Modifier.fillMaxWidth
和Modifier.height
方法指定轮播界面大小的Modifier
对象。Carousel
可组合函数通过将376.dp
值传递给Modifier.height
方法,设置 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
可组合函数会将一个组件放在另一个组件上。如需了解详情,请参阅布局基础知识。
如需显示背景图片,请按以下步骤操作:
- 先调用
AsyncImage
可组合函数,加载与Movie
对象关联的背景图片,然后再调用Text
可组合函数。 - 更新
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
可组合函数中调用Text
可组合函数,并使用R.string.show_details
调用的stringResource
函数的返回值。 - 在传递给
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
可组合函数中使用MaterialTheme.colorScheme.background
值分配backgroundColor
值 - 使用
Box
可组合函数封装Column
可组合函数 - 将
Alignment.BottomStart
值传递给Box
组件的contentAlignment
参数。 - 将
fillMaxSize
修饰符传递给Box
可组合函数的修饰符参数。fillMaxSize
修饰符是使用Modifier.fillMaxSize()
函数创建的。 - 对传递给
Box
可组合函数的fillMaxSize
修饰符调用drawBehind()
方法 - 在传递给
drawBehind
修饰符的 lambda 中,使用Brush
对象分配brush
值,该对象是通过调用Brush.linearGradient
函数(使用包含两个Color
对象的列表)创建的。上述列表是通过调用listOf
函数(使用backgroundColor
值和Color.Transparent
值)创建的。 - 使用传递给
drawBehind
修饰符的 lambda 中的brush
对象调用drawRect
,在背景图片上制作 srim 层 - 使用
padding
修饰符指定Column
可组合函数的内边距,该修饰符是通过调用Modifier.padding
(使用20.dp
值)创建的。 - 在
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 实现一个显示内容列表的页面。
- 用于显示内容详情的基本页面实现。
- 如何在两个页面之间添加页面转换。