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

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

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

Рисунок 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)
   
}
 
}
}