Skip to content

标签页与分页 (Tabs & Pager)

源:Compose 标签页

标签页用于在不同内容组之间进行切换。通常与 Pager(类似 ViewPager2)结合使用以实现滑动切换。

1. 基础标签页 (TabRow)

适用于 Tab 数量较少且固定的情况。

kotlin
var state by remember { mutableIntStateOf(0) }
val titles = listOf("推荐", "热门", "关注")

Column {
    TabRow(selectedTabIndex = state) {
        titles.forEachIndexed { index, title ->
            Tab(
                selected = state == index,
                onClick = { state = index },
                text = { Text(title) }
            )
        }
    }
    
    // 内容区域
    Text(
        modifier = Modifier.align(Alignment.CenterHorizontally).padding(16.dp),
        text = "当前页面: ${titles[state]}"
    )
}

2. 可滚动标签页 (ScrollableTabRow)

当 Tab 数量很多,或者 Tab 宽度不固定时使用。

kotlin
ScrollableTabRow(
    selectedTabIndex = state,
    edgePadding = 16.dp //起始位置的偏移量
) {
    titles.forEachIndexed { index, title ->
        Tab(
            selected = state == index,
            onClick = { state = index },
            text = { Text(title) }
        )
    }
}

3. Pager 与 Tabs 联动

这是最常见的场景。我们需要:

  1. 点击 Tab -> Pager 滚动到对应页。
  2. 滑动 Pager -> Tab 更新选中状态并滚动指示器。

依赖

HorizontalPager 现已移入 androidx.compose.foundation (Compose 1.4+),不再需要 Accompanist。

kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TabPagerExample() {
    val titles = listOf("首页", "视频", "游戏", "体育", "财经")
    // 1. 创建 Pager 状态
    val pagerState = rememberPagerState(pageCount = { titles.size })
    val scope = rememberCoroutineScope()

    Column {
        // 2. 绑定 TabRow
        ScrollableTabRow(
            selectedTabIndex = pagerState.currentPage,
            edgePadding = 0.dp,
            indicator = { tabPositions ->
                // 自定义指示器逻辑,使其跟随 Pager 偏移量平滑移动
                // 注意:这里需要配合 pagerState.currentPageOffsetFraction 使用
                // 为简化演示,这里使用默认的 TabRowDefaults.Indicator
                // 但要指定 modifier 为 tabPositions[pagerState.currentPage]
                TabRowDefaults.Indicator(
                    Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage])
                )
            }
        ) {
            titles.forEachIndexed { index, title ->
                Tab(
                    selected = pagerState.currentPage == index,
                    onClick = {
                        // 3. 点击 Tab 滚动 Pager
                        scope.launch {
                            pagerState.animateScrollToPage(index)
                        }
                    },
                    text = { Text(title) }
                )
            }
        }

        // 4. Pager 内容
        HorizontalPager(state = pagerState) { page ->
            Box(
                Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Text("Page $page: ${titles[page]}")
            }
        }
    }
}