카탈로그 브라우저 만들기

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

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

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

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

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

디스플레이에 표시되는 모든 항목은 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 함수는 섹션 표시 방법을 정의합니다. 다음 함수에서 TvLazyRowTvLazyColumn의 가로 버전을 제공된 DSL을 호출하여 TvLazyListScope.() -> Unit 블록으로 가로 목록을 정의하는 데 어떻게 이와 유사하게 사용되는지 보여줍니다.

@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 라이브러리에서도 제공됩니다. 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)
    }
  }
}