如何在Android Jetpack Compose(Material3)中实现可滑动自定义标签页
在Jetpack Compose中实现可滑动自定义标签页
一、核心实现思路
无需依赖第三方库,结合TabRow+HorizontalPager就能实现匹配需求的滑动标签页,同时可自定义样式与过渡动画,完全贴合你提供的示例效果。
二、完整代码实现
1. 依赖准备
确保项目build.gradle中引入必要依赖:
implementation "androidx.compose.material3:material3:1.2.1" implementation "androidx.compose.foundation:foundation:1.6.0" implementation "androidx.paging:paging-compose:3.3.0"
2. 带滑动动画的自定义标签页实现
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable fun CustomSlidingTabScreen() { val tabTitles = listOf("Songs", "Albums", "Artists", "Genres") val pagerState = rememberPagerState(pageCount = { tabTitles.size }) var selectedTabIndex by remember { mutableIntStateOf(0) } // 联动Pager与Tab选中状态 LaunchedEffect(pagerState.currentPage) { selectedTabIndex = pagerState.currentPage } // 自定义Tab栏 Box(modifier = Modifier.fillMaxWidth()) { LazyRow( modifier = Modifier .fillMaxWidth() .height(60.dp) .background(MaterialTheme.colorScheme.surface) .padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically ) { itemsIndexed(tabTitles) { index, title -> val isSelected = index == selectedTabIndex // Tab宽度动画 val tabWidth by animateDpAsState( targetValue = if (isSelected) 100.dp else 80.dp, animationSpec = tween(300), label = "tab width" ) Box( modifier = Modifier .padding(horizontal = 8.dp) .width(tabWidth) .height(40.dp) .clip(RoundedCornerShape(20.dp)) .background(if (isSelected) MaterialTheme.colorScheme.primaryContainer else Color.Transparent) .clickable { selectedTabIndex = index pagerState.animateScrollToPage(index) }, contentAlignment = Alignment.Center ) { Text( text = title, fontSize = 16.sp, fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal, color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) ) } } } // 底部滑动指示器 val indicatorOffset by animateDpAsState( targetValue = (selectedTabIndex * (100.dp + 16.dp)) + 24.dp, animationSpec = tween(300), label = "indicator offset" ) Box( modifier = Modifier .align(Alignment.BottomCenter) .offset(x = indicatorOffset) .width(80.dp) .height(3.dp) .clip(RoundedCornerShape(2.dp)) .background(MaterialTheme.colorScheme.primary) ) } // 内容Pager区域 HorizontalPager(state = pagerState, modifier = Modifier.fillMaxWidth()) { page -> Box( modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center ) { Text( text = "${tabTitles[page]} Content", fontSize = 20.sp, fontWeight = FontWeight.Medium ) } } }
三、关键效果说明
- 滑动切换:通过
HorizontalPager实现内容页左右滑动,同时自动同步Tab选中状态 - 过渡动画:利用
animateDpAsState实现Tab宽度变化、指示器平滑滑动的动画效果,与你提供的GIF效果一致 - 样式自定义:圆角Tab、选中态背景色、文字权重变化,完全匹配示例图的视觉风格
四、可选第三方库
如果希望快速实现,可选择成熟的Compose标签库:
- Accompanist Pager:官方推荐的扩展库,提供更丰富的Pager与Tab联动能力
- Compose Custom Tabs:第三方封装的预制组件,可直接调用实现类似效果
内容的提问来源于stack exchange,提问作者Mehedi Hasan
相关产品推荐
相关产品推荐

