创建目录浏览器

在 TV 上运行的媒体应用需要允许用户浏览其提供的内容、选择要播放的内容,以及开始播放内容。此类应用的内容浏览体验应简单直观,并且视觉上要赏心悦目。

媒体目录浏览器通常包含多个部分,每个部分都包含一个媒体内容列表。媒体目录中的版块示例包括:播放列表、精选内容、推荐类别。

图 1. 典型的目录界面。用户可以浏览视频目录数据。

使用 Compose for TV 提供的函数来实现一个用户界面,该界面可让用户浏览应用的媒体目录中包含的音乐或视频。

为目录创建可组合函数

在 Compose for TV 中,显示屏上显示的所有内容都以可组合函数的形式实现。首先,为媒体目录浏览器定义一个可组合函数:

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
// ToDo: add implementation
}

CatalogBrowser 是实现媒体目录浏览器的可组合函数。该函数接受以下实参:

  • 精选内容列表。
  • 部分列表。
  • Modifier 对象。
  • 一个回调函数,用于触发屏幕过渡。

设置界面元素

Compose for TV 提供了延迟列表,该组件可用于显示大量项(或长度未知的列表)。调用 LazyColumn 可垂直放置各个部分。LazyColumn 提供一个 LazyListScope.() -> Unit 块,该块提供一个用于定义列表项内容的 DSL。在以下示例中,每个部分都放置在垂直列表中,各部分之间有 16 dp 的间距:

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
  LazyColumn(
    modifier = modifier.fillMaxSize(),
    verticalArrangement = Arrangement.spacedBy(16.dp)
  ) {
    items(sectionList) { section ->
      Section(section, onItemSelected = onItemSelected)
    }
  }
}

在此示例中,Section 可组合函数定义了如何显示各个部分。 在以下函数中,LazyRow 展示了如何类似地使用 LazyColumn 的这种横向版本,通过调用提供的 DSL 来定义具有 LazyListScope.() -> Unit 块的横向列表:

@Composable
fun Section(
  section: Section,
  modifier: Modifier = Modifier,
  onItemSelected: (Movie) -> Unit = {},
) {
  Text(
    text = section.title,
    style = MaterialTheme.typography.headlineSmall,
  )
  LazyRow(
     modifier = modifier,
     horizontalArrangement = Arrangement.spacedBy(8.dp)
  ) {
    items(section.movieList){ movie ->
    MovieCard(
         movie = movie,
         onClick = { onItemSelected(movie) }
       )
    }
  }
}

Section 可组合项中,使用了 Text 组件。tv-material 库中提供了 Material Design 中定义的文本和其他组件。您可以参考 MaterialTheme 对象,按照 Material Design 中的定义更改文本的样式。此对象也由 tv-material 库提供。 Card 是 tv-material 库的一部分。MovieCard 定义了如何在目录中呈现每个电影数据,如下面的代码段所示:

@Composable
fun MovieCard(
   movie: Movie,
   modifier: Modifier = Modifier,
   onClick: () -> Unit = {}
) {
   Card(modifier = modifier, onClick = onClick){
    AsyncImage(
       model = movie.thumbnailUrl,
       contentDescription = movie.title,
     )
   }
}

在前面描述的示例中,所有电影的显示效果都一样。 它们具有相同的面积,在视觉上没有区别。 您可以使用 Carousel 突出显示部分内容。

轮播界面会以一组可滑动、淡入或移动到视图中的项目来显示信息。您可以使用此组件突出显示精选内容,例如新上线的电影或电视节目的新剧集。

Carousel 至少需要您指定轮播界面包含的项数以及如何绘制每个项。第一个可以使用 itemCount 指定。第二个可以作为 lambda 传递。系统会将所显示商品的索引号传递给 lambda。您可以使用给定的索引值确定显示的商品:

@Composable
function FeaturedCarousel(
  featuredContentList: List<Movie>,
  modifier: Modifier = Modifier,
) {
  Carousel(
    itemCount = featuredContentList.size,
    modifier = modifier,
  ) { index ->
    val content = featuredContentList[index]
    Box {
      AsyncImage(
        model = content.backgroundImageUrl,
        contentDescription = content.description,
        placeholder = painterResource(
          id = R.drawable.placeholder
        ),
        contentScale = ContentScale.Crop,
        modifier = Modifier.fillMaxSize()
      )
      Text(text = content.title)
    }
  }
}

Carousel 可以是延迟列表的项,例如 TvLazyColumn。以下代码段展示了位于所有 Section 可组合函数顶部的 FeaturedCarousel 可组合函数:

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
  TvLazyColumn(
    modifier = modifier.fillMaxSize(),
    verticalArrangement = Arrangement.spacedBy(16.dp)
  ) {

    item {
      FeaturedCarousel(featuredContentList)
    }

    items(sectionList) { section ->
      Section(section, onItemSelected = onItemSelected)
    }
  }
}