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

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

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

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

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

Создайте composable-функцию для каталога

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

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

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

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

Настройка элементов интерфейса

В 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.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) }
            )
        }
    }
}

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