You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:52:57