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

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

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

Рисунок 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.size) { index ->
            val section = sectionList[index]
            SectionRow(section, onItemSelected = onItemSelected)
        }
    }
}

В примере составная функция 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 . Компоненты 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 ожидает, что вы укажете как минимум количество элементов в Carousel и способ отрисовки каждого элемента. Первое можно указать с помощью itemCount . Второе можно передать в виде лямбда-функции. В лямбда-функцию передается индекс отображаемого элемента. Вы можете определить отображаемый элемент по заданному индексу:

Carousel может быть частью ленивого списка, например, LazyColumn . Следующий фрагмент кода демонстрирует, как элемент FeaturedCarousel располагается поверх всех элементов SectionRow :