在Jetpack Compose Material中调整TabRow指示器宽度
自定义Jetpack Compose TabRow的指示器宽度
你提到TabRow默认指示器会占满选中Tab的整个宽度,想让它适配文本长度,且已经在ScrollableTabRow上实现了这个效果,下面针对TabRow的场景给出可行方案:
核心思路
TabRow的每个Tab默认会均分父布局宽度,直接用tabIndicatorOffset会让指示器跟着Tab的宽度走。要让指示器适配文本长度,核心是获取Tab内Text的实际尺寸,再调整指示器的位置和宽度。
修改后的代码实现
Column { val tabs = listOf("Tab 1", "Tab 2", "Tab 3") // 存储每个Tab文本的宽度和在Tab内的偏移量 val textSizeStates = remember { mutableStateListOf<IntSize>() } val textOffsetStates = remember { mutableStateListOf<Offset>() } TabRow( selectedTabIndex = pagerState.currentPage, backgroundColor = MaterialTheme.colors.background, divider = { CustomDivider() }, contentColor = MaterialTheme.colors.primary, indicator = { tabPositions -> val selectedTabPosition = tabPositions[pagerState.currentPage] val textSize = textSizeStates.getOrElse(pagerState.currentPage) { IntSize.Zero } val textOffset = textOffsetStates.getOrElse(pagerState.currentPage) { Offset.Zero } // 计算指示器的偏移:Tab的起始偏移 + 文本在Tab内的水平偏移 val indicatorOffset = selectedTabPosition.left + textOffset.x Box( modifier = Modifier .offset(x = indicatorOffset.dp) .width(textSize.width.dp) .height(2.dp) .background(selectedTabIndicatorColor) ) } ) { tabs.forEachIndexed { index, title -> CompositionLocalProvider(LocalRippleTheme provides CustomRippleTheme) { Tab( text = { Text( title, color = if (pagerState.currentPage == index) selectedTabTextColor else unselectedTabTextColor, fontWeight = if (pagerState.currentPage == index) FontWeight.Bold else FontWeight.Normal, // 获取文本的实际尺寸和在Tab内的位置 onTextLayout = { result -> textSizeStates.apply { if (size > index) set(index, result.size) else add(result.size) } textOffsetStates.apply { if (size > index) set(index, result.getBoundingBox(0).topLeft) else add(result.getBoundingBox(0).topLeft) } } ) }, selected = pagerState.currentPage == index, selectedContentColor = Color.Gray, onClick = { scope.launch { pagerState.animateScrollToPage(index) } } ) } } } }
关键说明
- 通过
Text的onTextLayout回调,获取文本的实际宽高(result.size)和在Tab内的偏移位置(result.getBoundingBox(0).topLeft) - 在指示器中,结合Tab的起始偏移(
selectedTabPosition.left)和文本的水平偏移,计算出指示器的正确位置 - 将指示器的宽度设置为文本的实际宽度,实现和文本长度匹配的效果
如果需要固定宽度的指示器,直接把width(textSize.width.dp)改成固定值(比如width(20.dp)),同时调整偏移让它居中:
// 固定宽度指示器的偏移计算:Tab中心 - 指示器宽度的一半 val fixedWidth = 20.dp val indicatorOffset = selectedTabPosition.left + (selectedTabPosition.width - fixedWidth) / 2 Box( modifier = Modifier .offset(x = indicatorOffset) .width(fixedWidth) .height(2.dp) .background(selectedTabIndicatorColor) )
内容的提问来源于stack exchange,提问作者Sharath
相关产品推荐
相关产品推荐

