Создать браузер каталога

Медиа-приложение, работающее на телевизоре, должно позволять пользователям просматривать предложения контента, делать выбор и начинать воспроизведение контента. Опыт просмотра контента для приложений этого типа должен быть простым, интуитивно понятным, визуально приятным и привлекательным.

Браузер медиа-каталога обычно состоит из нескольких разделов, и в каждом разделе есть список медиа-контента. Примеры разделов медиакаталога: плейлисты, избранный контент, рекомендуемые категории.

Рисунок 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 — это составная функция, реализующая браузер медиа-каталога. Функция принимает следующие аргументы:

  • Список рекомендуемого контента.
  • Список разделов.
  • Объект модификатора.
  • Функция обратного вызова, которая запускает переход экрана.

Установить элементы пользовательского интерфейса

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 аналогичным образом используется для определения горизонтального списка с помощью LazyListScope.() -> Unit путем вызова предоставленного DSL:

@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 . Текст и другие компоненты, определенные в Material Design, предлагаются в библиотеке tv-material. Вы можете изменить стиль текста, определенный в Material Design, обратившись к объекту MaterialTheme . Этот объект также предоставляется библиотекой телевизионных материалов. Card является частью библиотеки ТВ-материалов. 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 ожидает, что вы как минимум укажете количество элементов в Carousel и способ рисования каждого элемента. Первый можно указать с помощью itemCount . Второй можно передать как лямбду. Порядковый номер отображаемого элемента передается лямбде. Вы можете определить отображаемый элемент по заданному значению индекса:

@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 . В следующем фрагменте показано, как можно компоновать FeaturedCarousel поверх всех компонуемых Section :

@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)
    }
  }
}