본문 바로가기
📱| Android/🚀 | Jetpack

[Android, Kotlin] compose로 tablayout 구현하기

by immgga 2023. 8. 15.

 

이번에 youtube open api를 다루면서 xml에서 사용했던 tablayout을 적용하고 싶어서 이번 포스팅에서는 compose tablayout 기능을 구현해 보도록 하겠다.

 

compose에서 tablayout 기능을 구현할 수 있게 해주는 composable function은 tabRow이다.

tabrow는 list를 이용해 화면(tab) 간의 이동을 가능하게 해주는 기능으로 bottom navigation과 비슷하다.

이번에 tabrow를 사용해 보았는데 기존에 xml에서는 screen마다 fragment를 필수적으로 생성해서 연결해야 했었는데, compose에서는 따로 screen을 만들지 말지 개발자가 정할 수 있어서 편했던 것 같다.

이번 기회에 필자는 screen을 생성하지 않은 tabrow를 구현해 보았다.

 

이제부터 그것을 어떻게 구현했는지 코드를 통해 확인해 보도록 하자.

 

그전에 tabrow에 대한 compose dependency를 추가해야 한다.// 2023/08/15 기준
// 2023/08/15 기준
implementation("com.google.accompanist:accompanist-pager:0.28.0")
implementation("com.google.accompanist:accompanist-pager-indicators:0.28.0")​

 

1. tabRow 생성하기

일단 먼저 tabrow를 생성해 주겠다.

 

page 구성 코드

val pages = listOf("괴물쥐", "노래", "리그 오브 레전드", "요리", "게임", "item 6", "item 7")
val pagerState = rememberPagerState()

Column(modifier = Modifier.fillMaxSize()) {
    ClipTabRow(pages = pages, pagerState = pagerState)

    ClipHorizontalPager(pages = pages, pagerState = pagerState)
}

tabrow와 horizontalPager를 이용해 카테고리별 검색 결과를 불러와보도록 하겠다. (item 6과 7은 임시 데이터로 넣어주었다.)

horizontalPager에 대해서는 tabRow 다음에 알아보도록 하겠다.

 

먼저 tabRow 부분 코드를 보자

pagerState를 저장한 변수를 만들어서 function param으로 가져왔다.

@Composable
@OptIn(ExperimentalPagerApi::class)
fun ClipTabRow(
    pages: List<String>,
    pagerState: PagerState
) {
    val coroutineScope = rememberCoroutineScope()

    ScrollableTabRow(
        backgroundColor = Color.White,
        selectedTabIndex = pagerState.currentPage,
        indicator = { tabPositions ->
            TabRowDefaults.Indicator(
                modifier = Modifier.pagerTabIndicatorOffset(pagerState, tabPositions = tabPositions),
                color = Color(0xFF7C86DF)
            )
        },
        edgePadding = 0.dp
    ) {
        pages.forEachIndexed { index, text ->
            Tab(
                selected = pagerState.currentPage == index,
                onClick = {
                    coroutineScope.launch {
                        pagerState.animateScrollToPage(index)
                    }
                },
                text = { Text(text = text) },
                selectedContentColor = Color(0xFF7C86DF),
                unselectedContentColor = Color.LightGray
            )
        }
    }
}

 

tabrow에 종류는 기본 tabRow와 현재 필자가 사용한 scrollableTabRow가 있는데 둘의 차이는 scrollableRow는 item이 많아서 핸드폰 크기를 넘어가면 옆으로 밀어서 다른 아이템을 확인할 수 있다.

 

아래의 gif를 보면 그냥 tabRow와 scrollableTabRow의 차이를 이해하기가 쉬울 것이다.

indicator는 무엇이냐면 특정 item이 클릭되었을 때 나타나는 밑줄(?)을 indicator라고 하는데 필자는 기본 indicator을 사용했다. indicator는 custom이 가능하다.

edgePadding 정확히 무슨 역할을 하는지는 필자도 모르겠지만 0dp로 설정을 안 하면 tabRow의 양 끝이 padding이 되는 광경을 볼 수 있을 것이다.

출처:&nbsp;https://proandroiddev.com/jetpack-compose-synchronize-lazyliststate-with-scrollabletabrow-22ff1f8577dc

 

이어서 설명을 하자면 아까 생성한 list를 이용해 foreach를 사용해서 Tab()을 만들어 주었다.

Tab composable function은 말 그대로 각 tab들을 구분 짓기 위한 것들이라 생각하면 되겠다.(위 사진의 category1, category2와 같은)

각 tab이 선택되었을 때, pagerState의 currentPage를 클릭한 tab의 index로 변경해 주는 로직을 만들었다.

그리고 coroutine scope를 만들어서 page 간 이동을 할 때 animation이 추가되도록 했다.

또한 selectedContentColor와 unSelectedContentColor를 이용해 각 item이 click 되었을 때와 그렇지 않을 때의 색을 정할 수 있다.

 

 

2. horizontalPager 생성하기

tabRow를 구현했으면 다음으로는 horizontalPager를 만들어야 한다.

사실 필자는 tabRow만으로 구현하는 것은 해본 적이 없어서 horizontalPager가 필수인지 선택인지는 모르겠지만 있어서 나쁠 건 없지 않은가? ㅋㅋ

pager는 다들 알다시피 tab 간의 이동을 tab을 click 해서 하는 게 아니라 옆으로 swipe 해서 이동할 수 있게 해주는 기능이다.

 

이제 horizontalPager의 코드를 확인해 보자.

@Composable
@OptIn(ExperimentalPagerApi::class)
fun ClipHorizontalPager(
    pages: List<String>,
    pagerState: PagerState
) {
    HorizontalPager(
        count = pages.size,
        state = pagerState
    ) { index ->
        Box(modifier = Modifier.fillMaxSize()) {
            SearchResultTab(pages = pages[index])
        }
    }
}

page의 count와 pagerState를 param으로 받는다.

저 두 개만 적용해 주면 사용하는 데에는 문제가 없을 것이다.

그다음으로 content 안에는 이제 본격적으로 tab의 화면을 구성해 주면 되겠다. 필자는 Box로 감싼 다음에 youtube api를 이용한 composable 함수(searchResultTab)를 만들어 주었다.

 

나중에 화면이 여러 개가 필요한 경우가 있을 때에는 index를 활용한 when 문을 이용해 screen을 변경하는 것도 가능할 것 같다.

 


정리

compose로 넘어오면서 xml 방식에서 tabRow를 구현하는 것보다 훨씬 쉬워진 것을 느꼈다.

 

scrollableTabRow를 사용해 보았을 때 화면에서 item tab을 클릭하면 그 item tab이 중앙으로 오도록 알아서 scroll 해주던데 보기가 좋았었다. 물론 비활성화할 수도 있겠지만 어떻게 하는지도 모르고 그냥 있어도 나쁠 건 없을 것 같다고 느꼈다.

 

이번에는 화면이 1개로 구성된 경우를 구현해 보았는데 추후에 또 쓸 일이 있을 때에는 화면이 여러 개가 필요한 경우도 있을 듯해서 화면이 여러 개 필요한 경우의 예제로도 연습을 해봐야겠다.

728x90

댓글