카탈로그 브라우저 만들기

TV에서 실행되는 미디어 앱은 사용자가 콘텐츠 서비스를 탐색하고, 콘텐츠를 선택하고, 콘텐츠 재생을 시작할 수 있도록 허용해야 합니다. 이러한 유형의 앱의 콘텐츠 탐색 환경은 단순하고 직관적이며 시각적으로 즐겁고 매력적이어야 합니다.

이 섹션에서는 TV용 Compose에서 제공하는 함수를 사용하여 앱의 미디어 카탈로그에서 음악이나 동영상을 탐색하기 위한 사용자 인터페이스를 구현하는 방법을 설명합니다.

그림 1. 일반적인 카탈로그 화면 사용자가 동영상 카탈로그 데이터를 둘러볼 수 있습니다.

미디어 카탈로그 브라우저는 여러 섹션으로 구성되는 경향이 있으며 각 섹션에는 미디어 콘텐츠 목록이 있습니다. 미디어 카탈로그 섹션의 예로는 재생목록, 추천 콘텐츠, 추천 카테고리 등이 있습니다

카탈로그용 구성 가능한 함수 만들기

디스플레이에 표시되는 모든 내용은 TV용 Compose에서 구성 가능한 함수로 구현됩니다. 먼저 미디어 카탈로그 브라우저의 구성 가능한 함수를 다음 스니펫으로 정의합니다.

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

CatalogBrowser는 미디어 카탈로그 브라우저를 구현하는 구성 가능한 함수입니다. 이 함수는 다음 인수를 사용합니다.

  • 추천 콘텐츠 목록입니다.
  • 섹션 목록입니다.
  • 수정자 객체입니다.
  • 화면 전환을 트리거하는 콜백 함수.

UI 요소 설정

TV용 Compose는 많은 수의 항목 (또는 길이를 알 수 없는 목록)을 표시하는 구성요소인 지연 목록을 제공합니다. TvLazyColumn를 호출하여 섹션을 세로로 배치합니다. TvLazyColumn는 항목 콘텐츠를 정의하는 DSL을 제공하는 TvLazyListScope.() -> Unit 블록을 제공합니다. 다음 예에서는 각 섹션이 섹션 간 간격이 16dp인 세로 목록에 배치됩니다.

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
  TvLazyColumn(
    modifier = modifier.fillMaxSize(),
    verticalArrangement = Arrangement.spacedBy(16.dp)
  ) {
    items(sectionList) { section ->
      Section(section, onItemSelected = onItemSelected)
    }
  }
}

이 예에서 구성 가능한 Section 함수는 섹션을 표시하는 방법을 정의합니다. 다음 함수에서 TvLazyRow는 제공된 DSL을 호출하여 TvLazyListScope.() -> Unit 블록이 있는 가로 목록을 정의하는 데 이 TvLazyColumn의 가로 버전이 유사하게 사용되는 방법을 보여줍니다.

@Composable
fun Section(
  section: Section,
  modifier: Modifier = Modifier,
  onItemSelected: (Movie) -> Unit = {},
) {
  Text(
    text = section.title,
    style = MaterialTheme.typography.headlineSmall,
  )
  TvLazyRow(
     modifier = modifier,
     horizontalArrangement = Arrangement.spacedBy(8.dp)
  ) {
    items(section.movieList){ movie ->
    MovieCard(
         movie = movie,
         onClick = { onItemSelected(movie) }
       )
    }
  }
}

Section 컴포저블에서는 Text 구성요소가 사용됩니다. Material Design에 정의된 텍스트 및 기타 구성요소는 tv-material 라이브러리에서 제공됩니다 . MaterialTheme 객체를 참조하여 Material Design에 정의된 대로 텍스트의 스타일을 변경할 수 있습니다. 이 객체는 tv-material 라이브러리에서도 제공됩니다. MovieCard는 다음 스니펫으로 정의된 카탈로그에서 각 영화 데이터가 렌더링되는 방식을 정의합니다. Card도 tv-material 라이브러리의 일부입니다.

@Composable
fun MovieCard(
   movie: Movie,
   modifier: Modifier = Modifier,
   onClick: () -> Unit = {}
) {
   Card(modifier = modifier, onClick = onClick){
    AsyncImage(
       model = movie.thumbnailUrl,
       contentDescription = movie.title,
     )
   }
}

앞에서 설명한 예에서는 모든 영화가 동일하게 표시됩니다. 이 둘은 같은 영역을 갖지만 시각적인 차이가 없습니다. Carousel를 사용하여 일부 옵션을 강조 표시할 수 있습니다.

캐러셀은 슬라이드하거나 페이드 아웃하거나 보기로 이동할 수 있는 항목 세트에 정보를 표시합니다. 구성요소를 사용하여 신작 영화나 TV 프로그램의 새 에피소드와 같은 추천 콘텐츠를 강조 표시할 수 있습니다.

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

CarouselTvLazyColumn와 같이 지연 목록의 항목일 수 있습니다. 다음 스니펫은 모든 Section 컴포저블 위에 FeaturedCarousel 컴포저블을 보여줍니다.

@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)
    }
  }
}