
xml에서는 viewpager을 구현하려면 어댑터도 구현해야하고 디자인 레이아웃도 따로 디자인해야하고....굉장히 귀찮았다.
그런데 컴포즈를 이용해 탭간 이동을 구현하는 방법은 생각보다 간단했다!
본 영상을 참고해서 구현하였다.
참고한 영상
탭으로 이동하는 화면을 만들기 위해서는 크게 TabRow() 밑에 HorizontalPager을 구현하면 된다.
enum class MyTab(val title:String) {
HOME("홈"),
PROFILE("프로필"),
LIST("리스트"),
SETTING("설정")
}
정의할 탭들을 enum class로 만들어 준다.
val scope = rememberCoroutineScope()
val pagerState = rememberPagerState(pageCount = {MyTab.values().size})
val selectedTabIndex = remember{ derivedStateOf { pagerState.currentPage } }
구현하기 앞서 이 3가지 변수를 정의해주어야한다.
rememberCoroutineScope: 비동기 작업을 실행할 때 사용하는 것이다. HorizontalPager사용시 비동기 처리를 통해 페이지를 변경하여 사용자에게 정보를 제공해야하므로 정의하였다.
rememberPagerState(): HorizontalPager의 상태를 관리하는 객체를 생성하고 기억한다. 이 상태에서는 페이지 인덱스, 스크롤 위치, 페이지 수 등의 정보를 포함할 수 있다. 이것으로 현재 보이는 페이지를 추적하고 사용자가 스와이프시 페이지 변경을 해야할때 필요한 정보를 제공한다.
selectedTabIndex: HorizontalPager의 페이지 인덱스를 나타내는 상태이다. 사용자가 페이지를 스와이프하여 변경하면 동기화하여 현재 위치한 페이지 인덱스를 표시할 수 있다.
TabRow(
selectedTabIndex = selectedTabIndex.value,
modifier = Modifier.fillMaxWidth()
) {
MyTab.values().forEachIndexed{index, currentTab->
Tab(
selected = selectedTabIndex.value == index,
selectedContentColor = Color.Yellow,
unselectedContentColor = Color.DarkGray,
onClick = {
scope.launch {
pagerState.animateScrollToPage(currentTab.ordinal)
}
},
text = {Text(text = currentTab.title)}
)
}
}
TabRow를 정의한다.
이 컨테이너는 탭들을 수평으로 배열하는 기능을 한다.
selectedTabIndex는 현재 선택된 탭의 이니덱스를 전달하며 어떤 탭이 활성화 상태인지를 표시한다. 또한 forEachIndexed는 탭과 해당 인덱스를 반복 처리한다.
onClick 을 통해 탭을 클릭했을 때 실행할 로직을 정의한다. 여기서는 scope.launch를 통해 코루틴을 시작하고
pagerState.animateScrollToPage(currentTab.ordinal) 를 호출해 해당 탭에 해당하는 페이지로 스무스하게 스크롤한다.
currentTab.ordinal 은 currentTab이 enum 클래스에 정의된 순서를 반환한다. 이는 뒤에 있을 HorizontalPager에서 연동하는데 사용된다.
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
){
Text(text = MyTab.values().get(selectedTabIndex.value).title)
}
}
HorizontalPager를 정의하여 스와이프 가능한 페이지를 정의한다. 이때 같은 selectedTabIndex 변수를 사용해 tabRow와 연동되는 페이지를 만들 수 있다.
❗️추가 커스텀❗️
탭을 누를 때마다 ripple 현상이 발생해 Tab의 매개변수 중 interactionSource를 null로 정의해주었다.
(구글은 왜 항상 디폴트로 ripple을 넣을까 모르겠다...)
interactionSource = null,
탭 할때마다 움직이는 바의 색상도 커스텀하기 위해 초록색으로 정의해주었다.
contentColor = Color.Green