오늘의 공부노트는 프로젝트에 많이 사용되는 스와이프 탭 UI 만드는 법을 적어보겠다!
우선 TabRow, HorizontalPager 각각 컴포넌트를 알아야 할 수 있기 때문에 먼저 각 컴포넌트를 알아보자.
본 예제는 Material Design3을 따른다.
# TabRow
- 여러 개의 Tab을 가로로 배치하고, 현재 선택된 탭을 시각적으로 표시하는 컴포넌트

@Composable
@UiComposable
fun TabRow(
selectedTabIndex: Int,
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
indicator: @Composable @UiComposable
(tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
TabRowDefaults.Indicator(
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
)
},
divider: @Composable @UiComposable () -> Unit =
@Composable {
TabRowDefaults.Divider()
},
tabs: @Composable @UiComposable () -> Unit
)
위 코드는 TabRow의 파라미터들이다. 중요 파라미터를 알아보자!
🟢 selectedTabIndex: 필수. 현재 선택된 탭의 인덱스를 나타낸다.
🟢 contentColor: 선택. 콘텐츠의 색을 결정한다.
- 해당 콘텐츠 색상을 Red로 변경 시 아래 사진과 같이 변경
contentColor = Color.Red

🟢 backgroundColor: 선택. 배경 색을 결정한다.
- 해당 배경색을 White로 변경 시 아래 사진과 같이 변경
backgroundColor = Color.White,

🟢 indicator: 선택. 선택 표시줄을 원하는 스타일로 바꿀 때 사용

indicator: 선택된 탭 아래에 그려지는 시각적 포인터이다. 즉, 사용자가 "지금 어떤 탭이 선택되어 있는지" 한눈에 알아볼 수 있도록 해주는 시각적 장치이다.
위 사진의 빨간 부분을 말한다.
🟢 divider: 선택. TabRow의 전체 너비에 걸쳐 표시되는 선을 바꿀 때 사용

divider = { HorizontalDivider(color = Color.Red) },
- 해당 divider 색을 빨강으로 변경 시 위 사진과 같이 변경
indicator와 차이점은 indicator는 현재 선택되어 있는 탭 아래에 표시되는 선을 나타내고 divider은 TabRow의 전체 너비에 걸쳐 표시되는 선을 나타낸다.
🟢 tabs: 필수. TabRow 내부에 표시할 탭들을 정의하는 영역
tabs = {
repeat(4) { index ->
// 커스텀 방식
Text(text = "탭$index")
// 제공하는 Tab 컴포저블을 이용하는 방식
Tab(
selected = selectTabIdx == index,
onClick = { selectTabIdx = index},
text = { Text(text = "탭$index") }
)
}
}
TabRow의 들어갈 Tab 들을 정의 할 수 있는 방법이 2가지가 있다.
1. 커스텀 방식
2. 안드로이드에서 기본적으로 제공하는 Tab 컴포저블을 이용하는 방식
## 커스텀 방식
탭의 디자인을 완전히 자유롭게 구성할 수 있는 방식이다.
Tab 컴포저블을 사용하지 않고 Box, Text, Icon 등 일반 컴포저블들을 조합하여 원하는 형태의 탭을 만들 수 있다.
해당 방식의 단점으로는
- 작업하는 프로젝트가 Material Design 가이드라인을 따른다면 적합하지 않다.
- 클릭 기능이나 선택 상태 등 직접 구현해야 할 일이 생긴다.
## Tab 컴포저블 방식
안드로이드에서 기본적으로 제공하는 Tab 컴포저블을 사용하는 방식이다. 선택 상태 관리, 접근성, 터치 피드백 등이 자동으로 처리된다.
@Composable
fun Tab(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
text: @Composable (() -> Unit)? = null,
icon: @Composable (() -> Unit)? = null,
selectedContentColor: Color = LocalContentColor.current,
unselectedContentColor: Color = selectedContentColor,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
...
...
}
위는 Tab 컴포저블의 내부 구현이다.
보다시피 클릭 로직이나 활성, 선택 상태 등 이미 내부에서 구현을 해놨기 때문에 간편하다.
해당 방식의 단점으로는
- 내가 원하는 디자인을 사용할 수 없다는 디자인 커스터마이징에 제약이 있다.
<커스텀 방식>

<Tab 컴포저블 방식>

사진으로 보면 Tab 컴포저블 방식을 사용한 탭이 크기가 크다.
그 이유는 Tab 컴포저블 내부에서 Material 디자인 스펙과 텍스트 스타일(폰트 크기), 패딩 값에 따라 Tab 컴포저블 크기가 자동으로 계산되기 때문에 적절한 크기를 맞춰 준다.
‼️ 팁 Tab 컴포저블이지만 크기를 바꾸고 싶다면 Tab 컴포저블의 modifier를 사용하면 크기 변경이 가능하다.
공부하면서 느낀 점은 만약 TabRow 컴포저블의 존재를 몰랐다면 귀찮게 Row의 반복문을 작성해서 일일이 구현했을 것이다. UI 제작 전 해당 UI에 최적화된 컴포저블이 있는지 찾아보는 행위가 중요하다는 것을 느꼈다.
이렇게 1편으로 TabRow에 대해 알아봤다. 이제 2편에서는 해당 탭을 스와이프를 시켜주기 위한 HorizontalPager에 대해 알아볼 것이다.