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

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

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

Рисунок 1. Типичный экран каталога. Пользователи могут просматривать данные каталога видео.

Используйте функции, предоставляемые Compose для телевизоров, чтобы реализовать интерфейс для просмотра музыки или видео из каталога медиаконтента вашего приложения.

Как создать composable-функцию для каталога

Все, что появляется на экране, реализуется как composable-функция в Compose for TV. Начните с определения функции, которая будет использоваться для браузера каталога медиаконтента:

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

CatalogBrowser – это composable-функция, реализующая браузер медиаконтента. Функция принимает следующие аргументы:

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

Как задать элементы интерфейса

Compose для ТВ предлагает ленивые списки – компонент для показа большого количества объектов (или списка неизвестной длины). Вызовите метод 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.size) { index ->
            val section = sectionList[index]
            SectionRow(section, onItemSelected = onItemSelected)
        }
    }
}

В примере composable-функция SectionRow определяет, как отображать разделы. В приведенной ниже функции LazyRow демонстрирует, как горизонтальная версия LazyColumn используется для определения горизонтального списка с блоком LazyListScope.() -> Unit путем вызова предоставленного DSL:

@Composable
fun SectionRow(
    section: Section,
    modifier: Modifier = Modifier,
    onItemSelected: (Movie) -> Unit = {},
) {
    Text(
        text = section.title,
        style = MaterialTheme.typography.headlineSmall,
    )
    MovieCatalog(
        section.movieList,
        onClick = { movie -> onItemSelected(movie) },
        modifier
    )
}

@Composable
fun MovieCatalog(
    movies: List<Movie>,
    onClick: (Movie) -> Unit,
    modifier: Modifier = Modifier,
) {
    LazyRow(
        modifier = modifier,
        horizontalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(movies.size) { index ->
            val movie = movies[index]
            MovieCard(
                movie = movie,
                onClick = { onClick(movie) }
            )
        }
    }
}

В функции SectionRow используется компонент Text. Текст и другие компоненты, определенные в Material Design, предлагаются в библиотеке tv-material . Вы можете изменить стиль текста, заданный в Material Design, с помощью объекта MaterialTheme. Этот объект также предоставляется библиотекой tv-material. Card входит в библиотеку материалов для ТВ. MovieCard определяет, как данные о каждом фильме будут представлены в каталоге, который определяется следующим фрагментом кода:

@OptIn(ExperimentalMaterial3Api::class)
@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. Второй можно передать как лямбда-функцию. Лямбда-функции передается индекс отображаемого элемента. Определить, какой элемент будет показан, можно с помощью следующего индекса:

Carousel может быть элементом списка с отложенной инициализацией, например LazyColumn. В приведенном ниже фрагменте кода показано, как composable-функция FeaturedCarousel располагается поверх всех composable-функций SectionRow: