A media app that runs on a TV needs to allow users to browse its content offerings, make a selection, and start playing content. The content browsing experience for apps of this type should be simple, intuitive, and visually pleasing and engaging.
A media catalog browser tends to consist of several sections, and each section has a list of media content. Examples of sections in a media catalog include: playlists, featured content, recommended categories.
Use the functions provided by Compose for TV to implement a user interface for browsing music or videos from your app's media catalog.
Create a composable function for catalog
Everything appearing on a display is implemented as a composable function in Compose for TV. Start by defining a composable function for the media catalog browser:
@Composable
fun CatalogBrowser(
featuredContentList: List<Movie>,
sectionList: List<Section>,
modifier: Modifier = Modifier,
onItemSelected: (Movie) -> Unit = {},
) {
// ToDo: add implementation
}
CatalogBrowser
is the composable function implementing your media catalog
browser. The function takes the following arguments:
- List of featured content.
- List of sections.
- A Modifier object.
- A callback function, which triggers a screen transition.
Set UI elements
Compose for TV offers lazy lists, a component to display a large
number of items (or a list of an unknown length). Call
LazyColumn
to place sections vertically. LazyColumn
provides a
LazyListScope.() -> Unit
block, which offers a DSL to define item contents. In the following example,
each section is placed in a vertical list with a 16 dp gap between sections:
@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)
}
}
}
In the example, Section
composable function defines how to display sections.
In the following function, LazyRow
demonstrates how this
horizontal version of LazyColumn
is similarly used to
define a horizontal list with a LazyListScope.() -> Unit
block by calling
the provided 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) }
)
}
}
}
In the Section
composable, the Text
component is used.
Text and other components
defined in Material Design are offered in the tv-material library . You can
change the texts' style as defined in Material Design by referring to the
MaterialTheme
object.
This object is also provided by the tv-material library.
Card
is part of the tv-material library.
MovieCard
defines how each movie data is rendered in the catalog defined
as the following snippet:
@Composable
fun MovieCard(
movie: Movie,
modifier: Modifier = Modifier,
onClick: () -> Unit = {}
) {
Card(modifier = modifier, onClick = onClick){
AsyncImage(
model = movie.thumbnailUrl,
contentDescription = movie.title,
)
}
}
Highlight featured content
In the example described earlier, all movies are displayed equally.
They have the same area, no visual difference between them.
You can highlight some of them with Carousel
.
Carousel displays the information in a set of items that can slide, fade, or move into view. You use the component to highlight featured content, such as newly available movies or new episodes of TV programs.
Carousel
expects you to at least specify the number of items that Carousel has and how to
draw each item. The first one can be specified with itemCount
. The second one
can be passed as a lambda. The index number of the displayed item is
given to the lambda. You can determine the displayed item with the
given index value:
@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
can be an item of a lazy list, such as TvLazyColumn
.
The following snippet shows FeaturedCarousel
composable on top of the
all Section
composables:
@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)
}
}
}