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

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

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

Рисунок 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 . Этот объект также предоставляется библиотекой 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)
    }
  }
}
,

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

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

Рисунок 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 . Этот объект также предоставляется библиотекой 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)
    }
  }
}