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

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

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

Рисунок 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 composable определяет, как отображать разделы. В следующей функции 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 composable используется компонент Text . Text и другие компоненты, определенные в Material Design, предлагаются в библиотеке tv-material. Вы можете изменить стиль текстов, определенный в Material Design, обратившись к объекту MaterialTheme . Этот объект также предоставляется библиотекой 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 ожидает, что вы как минимум укажете количество элементов, которые есть в 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)
    }
  }
}