카탈로그 브라우저 만들기

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

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

그림 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 는 미디어 카탈로그 브라우저를 구현하는 구성 가능한 함수입니다. 이 함수는 다음 인수를 사용합니다.

  • 추천 콘텐츠 목록
  • 섹션 목록
  • Modifier 객체
  • 화면 전환을 트리거하는 콜백 함수

UI 요소 설정

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

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

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

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

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

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