Jetpack Compose中是否有类似ScrollableTabRow的ScrollableTabColumn?
响应式Tabs布局解决方案(Jetpack Compose)
核心思路就是根据屏幕尺寸自动切换横向/竖向Tabs布局,同时和你当前用的ScrollableTabRow参数完全对齐,让调用方无缝切换,不用改太多代码。
第一步:封装统一的响应式Tab容器
直接写一个ResponsiveTabRow组件,内部根据WindowSizeClass判断屏幕宽度,自动切换手机端的横向滚动Tab和平板端的竖向Tab列:
@Composable fun ResponsiveTabRow( selectedTabIndex: Int, modifier: Modifier = Modifier, backgroundColor: Color = MaterialTheme.colors.primarySurface, contentColor: Color = contentColorFor(backgroundColor), edgePadding: Dp = TabRowDefaults.ScrollableTabRowPadding, indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = { tabPositions -> TabRowDefaults.Indicator( Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]) ) }, divider: @Composable () -> Unit = { TabRowDefaults.Divider() }, tabs: @Composable () -> Unit, windowSizeClass: WindowSizeClass ) { when (windowSizeClass.widthSizeClass) { WidthSizeClass.Compact -> { // 手机端:直接用你原来的ScrollableTabRow ScrollableTabRow( selectedTabIndex = selectedTabIndex, modifier = modifier, backgroundColor = backgroundColor, contentColor = contentColor, edgePadding = edgePadding, indicator = indicator, divider = divider, tabs = tabs ) } else -> { // 平板/大屏:自定义竖向Tab列 Column( modifier = modifier .background(backgroundColor) .padding(vertical = edgePadding) ) { // 用自定义Layout计算每个Tab的位置,生成竖向的TabPosition val tabPositions = remember { mutableStateListOf<TabPosition>() } val density = LocalDensity.current Layout( content = tabs, modifier = Modifier.weight(1f) ) { measurables, constraints -> val placeables = measurables.map { it.measure(constraints.copy(minHeight = 0)) } var yOffset = 0 tabPositions.clear() placeables.forEach { placeable -> with(density) { tabPositions.add( TabPosition( left = 0.dp, top = yOffset.toDp(), width = placeable.width.toDp(), height = placeable.height.toDp() ) ) } yOffset += placeable.height } layout(constraints.maxWidth, yOffset) { var currentY = 0 placeables.forEach { placeable -> placeable.place(0, currentY) currentY += placeable.height } } } // 指示器和分割线,适配竖向布局 Column(modifier = Modifier.fillMaxWidth()) { indicator(tabPositions) divider() } } } } }
第二步:调用示例
直接在你的页面中使用这个组件,传入WindowSizeClass即可:
@Composable fun MainScreen(windowSizeClass: WindowSizeClass) { val selectedTab = remember { mutableStateOf(0) } val tabTitles = listOf("首页", "分类", "我的", "设置", "收藏") ResponsiveTabRow( selectedTabIndex = selectedTab.value, windowSizeClass = windowSizeClass ) { tabTitles.forEachIndexed { index, title -> Tab( selected = selectedTab.value == index, onClick = { selectedTab.value = index }, text = { Text(title) } ) } } // 对应Tab的内容区域 when (selectedTab.value) { 0 -> Text("首页内容") 1 -> Text("分类内容") 2 -> Text("我的内容") 3 -> Text("设置内容") 4 -> Text("收藏内容") } }
优化:自定义竖向指示器
默认的横向指示器在竖向布局里不太协调,你可以自己写个竖向的指示器替换:
@Composable fun VerticalTabIndicator( modifier: Modifier = Modifier, tabPosition: TabPosition, color: Color = MaterialTheme.colors.primary, width: Dp = 4.dp ) { Box( modifier = modifier .fillMaxHeight() .width(width) .offset(y = tabPosition.top) .background(color) ) }
调用时替换indicator参数就行:
ResponsiveTabRow( selectedTabIndex = selectedTab.value, windowSizeClass = windowSizeClass, indicator = { tabPositions -> VerticalTabIndicator( tabPosition = tabPositions[selectedTab.value] ) } ) { // ... Tab内容 }
内容的提问来源于stack exchange,提问作者Victor Cocuz
相关产品推荐
相关产品推荐

