
💫 坚果派·红目香薰 倾情分享
🎯 用心打造每一个技术细节,为开发者创造更多价值
📱 让HarmonyOS开发变得更简单、更有趣
嗨,亲爱的技术朋友们!👋
我是来自坚果派的红目香薰,一个热爱技术、专注HarmonyOS开发的程序媛。在这个数字化飞速发展的时代,HarmonyOS作为华为自主研发的操作系统,正在改变着我们的数字生活体验。
🌈 为什么要写这个系列?
每一个Demo都是我精心设计和反复测试的结果,希望能够为你的HarmonyOS开发之路点亮一盏明灯。✨
今天我们来深入学习HarmonyOS中最常用的导航组件之一——Tabs页签组件。从基础页签到炫酷的自定义样式,让我们一起打造优雅的页签导航体验!
本Demo展示了HarmonyOS中Tabs组件的全面使用方法,包括:
界面采用多样化的页签展示:
TabsDemo/
├── src/
│ ├── main/
│ │ ├── ets/
│ │ │ ├── pages/
│ │ │ │ └── Index.ets // 主页面
│ │ │ └── entryability/
│ │ └── resources/
│ │ ├── base/
│ │ │ ├── element/
│ │ │ └── media/
│ │ └── rawfile/
│ └── module.json5Tabs({ barPosition: BarPosition.End }) {
TabContent() {
// 页签内容
}
.tabBar('首页')
TabContent() {
// 页签内容
}
.tabBar('发现')
}
.width('100%')
.height('100%')TabContent() {
// 内容
}
.tabBar(this.TabBuilder(0, '首页', '🏠'))自定义TabBar:
使用@Builder装饰器创建自定义页签样式,支持图标、文字、徽章等元素。
页签切换动画:
通过animationDuration属性控制切换动画时长和效果。
响应式布局:
根据屏幕尺寸自动调整页签布局和样式。
interface TabItemData {
id: number
title: string
icon: string
badge?: number
content: string
}
interface NewsItemData {
id: number
title: string
summary: string
time: string
category: string
}
@Entry
@Component
struct TabsDemo {
@State currentIndex: number = 0
@State selectedDemo: number = 0
@State tabItems: TabItemData[] = []
private bottomTabs: TabItemData[] = [
{ id: 1, title: '首页', icon: '🏠', content: '欢迎来到首页,这里展示最新的内容和推荐信息。' },
{ id: 2, title: '发现', icon: '🔍', badge: 3, content: '发现更多精彩内容,探索新的世界。' },
{ id: 3, title: '消息', icon: '💬', badge: 12, content: '查看最新消息和通知。' },
{ id: 4, title: '我的', icon: '👤', content: '个人中心,管理您的账户和设置。' }
]
private topTabs: TabItemData[] = [
{ id: 1, title: '推荐', icon: '⭐', content: '为您精选的优质内容' },
{ id: 2, title: '科技', icon: '💻', content: '最新科技资讯和趋势' },
{ id: 3, title: '娱乐', icon: '🎬', content: '娱乐八卦和明星动态' },
{ id: 4, title: '体育', icon: '⚽', content: '体育赛事和运动资讯' },
{ id: 5, title: '财经', icon: '💰', content: '财经新闻和市场分析' }
]
private newsData: NewsItemData[] = [
{ id: 1, title: 'HarmonyOS 4.0正式发布', summary: '华为发布全新操作系统,带来更多创新功能', time: '2小时前', category: '科技' },
{ id: 2, title: '人工智能技术新突破', summary: 'AI技术在各个领域取得重大进展', time: '4小时前', category: '科技' },
{ id: 3, title: '新电影票房创纪录', summary: '暑期档电影市场表现亮眼', time: '6小时前', category: '娱乐' },
{ id: 4, title: '奥运会精彩瞬间回顾', summary: '运动员们的精彩表现令人难忘', time: '8小时前', category: '体育' }
]
aboutToAppear() {
this.tabItems = [...this.bottomTabs]
}
build() {
Column() {
this.buildTitleSection()
this.buildDemoSelector()
if (this.selectedDemo === 0) {
this.buildBottomTabs()
} else if (this.selectedDemo === 1) {
this.buildTopTabs()
} else if (this.selectedDemo === 2) {
this.buildVerticalTabs()
} else {
this.buildCustomTabs()
}
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
@Builder
buildTitleSection() {
Column({ space: 10 }) {
Text('📑 HarmonyOS Tabs页签组件')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.width('100%')
.padding(20)
.borderRadius(15)
.linearGradient({
angle: 45,
colors: [['#667eea', 0.0], ['#764ba2', 1.0]]
})
.shadow({
radius: 10,
color: '#40000000',
offsetX: 0,
offsetY: 5
})
Text('打造优雅的页签导航体验')
.fontSize(14)
.fontColor('#666666')
.textAlign(TextAlign.Center)
.fontStyle(FontStyle.Italic)
}
.width('100%')
.backgroundColor(Color.White)
.padding(20)
.borderRadius(12)
.margin({ bottom: 10 })
.shadow({
radius: 8,
color: '#20000000',
offsetX: 0,
offsetY: 2
})
}
@Builder
buildDemoSelector() {
Row({ space: 5 }) {
Button('底部页签')
.width(70)
.height(35)
.fontSize(12)
.backgroundColor(this.selectedDemo === 0 ? '#3498DB' : '#ECF0F1')
.fontColor(this.selectedDemo === 0 ? Color.White : '#7F8C8D')
.borderRadius(8)
.onClick(() => {
this.selectedDemo = 0
})
Button('顶部页签')
.width(70)
.height(35)
.fontSize(12)
.backgroundColor(this.selectedDemo === 1 ? '#3498DB' : '#ECF0F1')
.fontColor(this.selectedDemo === 1 ? Color.White : '#7F8C8D')
.borderRadius(8)
.onClick(() => {
this.selectedDemo = 1
})
Button('垂直页签')
.width(70)
.height(35)
.fontSize(12)
.backgroundColor(this.selectedDemo === 2 ? '#3498DB' : '#ECF0F1')
.fontColor(this.selectedDemo === 2 ? Color.White : '#7F8C8D')
.borderRadius(8)
.onClick(() => {
this.selectedDemo = 2
})
Button('自定义')
.width(70)
.height(35)
.fontSize(12)
.backgroundColor(this.selectedDemo === 3 ? '#3498DB' : '#ECF0F1')
.fontColor(this.selectedDemo === 3 ? Color.White : '#7F8C8D')
.borderRadius(8)
.onClick(() => {
this.selectedDemo = 3
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({ left: 20, right: 20, bottom: 10 })
}
@Builder
buildBottomTabs() {
Tabs({ barPosition: BarPosition.End }) {
ForEach(this.bottomTabs, (item: TabItemData, index: number) => {
TabContent() {
this.buildTabContentPage(item, index)
}
.tabBar(this.BottomTabBuilder(index, item.title, item.icon, item.badge))
})
}
.width('100%')
.layoutWeight(1)
.barBackgroundColor('#FFFFFF')
.barHeight(80)
.animationDuration(300)
.onChange((index: number) => {
this.currentIndex = index
})
}
@Builder
buildTopTabs() {
Tabs({ barPosition: BarPosition.Start }) {
ForEach(this.topTabs, (item: TabItemData, index: number) => {
TabContent() {
this.buildNewsContentPage(item, index)
}
.tabBar(this.TopTabBuilder(index, item.title))
})
}
.width('100%')
.layoutWeight(1)
.barBackgroundColor('#FFFFFF')
.barHeight(50)
.scrollable(true)
.animationDuration(200)
.onChange((index: number) => {
this.currentIndex = index
})
}
@Builder
buildVerticalTabs() {
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
this.buildSettingsPage('账户设置', '管理您的账户信息和安全设置')
}
.tabBar(this.VerticalTabBuilder(0, '账户设置', '👤'))
TabContent() {
this.buildSettingsPage('隐私设置', '控制您的隐私和数据使用权限')
}
.tabBar(this.VerticalTabBuilder(1, '隐私设置', '🔒'))
TabContent() {
this.buildSettingsPage('通知设置', '管理应用通知和提醒设置')
}
.tabBar(this.VerticalTabBuilder(2, '通知设置', '🔔'))
TabContent() {
this.buildSettingsPage('显示设置', '调整界面显示和主题设置')
}
.tabBar(this.VerticalTabBuilder(3, '显示设置', '🎨'))
TabContent() {
this.buildSettingsPage('关于应用', '查看应用版本和相关信息')
}
.tabBar(this.VerticalTabBuilder(4, '关于应用', 'ℹ️'))
}
.width('100%')
.layoutWeight(1)
.vertical(true)
.barWidth(120)
.barBackgroundColor('#F8F9FA')
.animationDuration(250)
.onChange((index: number) => {
this.currentIndex = index
})
}
@Builder
buildCustomTabs() {
Column({ space: 20 }) {
Column({ space: 15 }) {
Text('🎴 卡片式页签')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.alignSelf(ItemAlign.Start)
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
this.buildCardContent('数据概览', '📊', '#3498DB')
}
.tabBar(this.CardTabBuilder(0, '概览', '📊', '#3498DB'))
TabContent() {
this.buildCardContent('用户分析', '👥', '#E74C3C')
}
.tabBar(this.CardTabBuilder(1, '用户', '👥', '#E74C3C'))
TabContent() {
this.buildCardContent('销售报表', '💰', '#27AE60')
}
.tabBar(this.CardTabBuilder(2, '销售', '💰', '#27AE60'))
TabContent() {
this.buildCardContent('系统设置', '⚙️', '#9B59B6')
}
.tabBar(this.CardTabBuilder(3, '设置', '⚙️', '#9B59B6'))
}
.width('100%')
.height(300)
.barBackgroundColor('transparent')
.barHeight(60)
.animationDuration(300)
}
.width('100%')
.backgroundColor(Color.White)
.padding(20)
.borderRadius(12)
.shadow({
radius: 8,
color: '#20000000',
offsetX: 0,
offsetY: 2
})
Column({ space: 15 }) {
Row() {
Text('🔄 动态页签')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.layoutWeight(1)
Button('➕ 添加')
.height(32)
.fontSize(12)
.backgroundColor('#27AE60')
.borderRadius(16)
.onClick(() => {
this.addTab()
})
}
.width('100%')
Tabs({ barPosition: BarPosition.Start }) {
ForEach(this.tabItems, (item: TabItemData, index: number) => {
TabContent() {
this.buildDynamicTabContent(item, index)
}
.tabBar(this.DynamicTabBuilder(index, item.title, item.icon))
})
}
.width('100%')
.height(250)
.barBackgroundColor('#F8F9FA')
.barHeight(50)
.scrollable(true)
.animationDuration(200)
}
.width('100%')
.backgroundColor(Color.White)
.padding(20)
.borderRadius(12)
.shadow({
radius: 8,
color: '#20000000',
offsetX: 0,
offsetY: 2
})
}
.layoutWeight(1)
.padding({ left: 20, right: 20 })
}
@Builder
BottomTabBuilder(index: number, title: string, icon: string, badge?: number) {
Column({ space: 5 }) {
Stack({ alignContent: Alignment.TopEnd }) {
Text(icon)
.fontSize(24)
.fontColor(this.currentIndex === index ? '#3498DB' : '#999999')
if (badge && badge > 0) {
Text(badge > 99 ? '99+' : badge.toString())
.fontSize(10)
.fontColor(Color.White)
.backgroundColor('#E74C3C')
.borderRadius(8)
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
.margin({ top: -5, right: -5 })
}
}
Text(title)
.fontSize(12)
.fontColor(this.currentIndex === index ? '#3498DB' : '#999999')
.fontWeight(this.currentIndex === index ? FontWeight.Medium : FontWeight.Normal)
}
.width(60)
.height(60)
.justifyContent(FlexAlign.Center)
}
@Builder
TopTabBuilder(index: number, title: string) {
Column({ space: 5 }) {
Text(title)
.fontSize(16)
.fontColor(this.currentIndex === index ? '#3498DB' : '#666666')
.fontWeight(this.currentIndex === index ? FontWeight.Bold : FontWeight.Normal)
.padding({ left: 15, right: 15 })
if (this.currentIndex === index) {
Divider()
.width(30)
.height(3)
.color('#3498DB')
.borderRadius(2)
}
}
.height(50)
.justifyContent(FlexAlign.Center)
}
@Builder
VerticalTabBuilder(index: number, title: string, icon: string) {
Row({ space: 10 }) {
Text(icon)
.fontSize(20)
.fontColor(this.currentIndex === index ? '#3498DB' : '#666666')
Text(title)
.fontSize(14)
.fontColor(this.currentIndex === index ? '#3498DB' : '#666666')
.fontWeight(this.currentIndex === index ? FontWeight.Medium : FontWeight.Normal)
.layoutWeight(1)
}
.width('100%')
.height(50)
.padding({ left: 15, right: 15 })
.backgroundColor(this.currentIndex === index ? '#E3F2FD' : 'transparent')
.borderRadius(8)
.justifyContent(FlexAlign.Start)
}
@Builder
CardTabBuilder(index: number, title: string, icon: string, color: string) {
Column({ space: 8 }) {
Text(icon)
.fontSize(20)
.fontColor(this.currentIndex === index ? Color.White : color)
Text(title)
.fontSize(12)
.fontColor(this.currentIndex === index ? Color.White : color)
.fontWeight(FontWeight.Medium)
}
.width(80)
.height(50)
.backgroundColor(this.currentIndex === index ? color : '#F8F9FA')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.shadow(this.currentIndex === index ? {
radius: 8,
color: color + '40',
offsetX: 0,
offsetY: 4
} : undefined)
}
@Builder
DynamicTabBuilder(index: number, title: string, icon: string) {
Row({ space: 8 }) {
Text(icon)
.fontSize(16)
Text(title)
.fontSize(14)
.fontColor(this.currentIndex === index ? '#3498DB' : '#666666')
.fontWeight(this.currentIndex === index ? FontWeight.Medium : FontWeight.Normal)
if (this.tabItems.length > 1) {
Text('✕')
.fontSize(12)
.fontColor('#999999')
.onClick(() => {
this.removeTab(index)
})
}
}
.padding({ left: 12, right: 8, top: 8, bottom: 8 })
.backgroundColor(this.currentIndex === index ? '#E3F2FD' : 'transparent')
.borderRadius(20)
}
@Builder
buildTabContentPage(item: TabItemData, index: number) {
Column({ space: 30 }) {
Column({ space: 15 }) {
Text(item.icon)
.fontSize(64)
Text(item.title)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(item.content)
.fontSize(16)
.fontColor('#666666')
.textAlign(TextAlign.Center)
.lineHeight(24)
}
Column({ space: 15 }) {
Text('✨ 主要功能')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.alignSelf(ItemAlign.Start)
Grid() {
GridItem() {
this.buildFeatureCard('📊', '数据统计', '查看详细数据')
}
GridItem() {
this.buildFeatureCard('🔔', '消息通知', '管理通知设置')
}
GridItem() {
this.buildFeatureCard('⚙️', '系统设置', '个性化配置')
}
GridItem() {
this.buildFeatureCard('❓', '帮助中心', '获取使用帮助')
}
}
.columnsTemplate('1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(15)
.rowsGap(15)
.width('100%')
.height(200)
}
}
.width('100%')
.height('100%')
.padding(30)
.justifyContent(FlexAlign.Center)
}
@Builder
buildNewsContentPage(item: TabItemData, index: number) {
Column({ space: 20 }) {
Row({ space: 10 }) {
Text(item.icon)
.fontSize(24)
Text(`${item.title}频道`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
}
.width('100%')
.padding({ left: 20, right: 20, top: 20 })
List({ space: 15 }) {
ForEach(this.newsData.filter(news =>
item.title === '推荐' || news.category === item.title
), (news: NewsItemData) => {
ListItem() {
this.buildNewsItem(news)
}
})
}
.width('100%')
.layoutWeight(1)
.padding({ left: 20, right: 20 })
}
.width('100%')
.height('100%')
.backgroundColor('#F8F9FA')
}
@Builder
buildSettingsPage(title: string, description: string) {
Column({ space: 30 }) {
Column({ space: 15 }) {
Text('⚙️')
.fontSize(48)
Text(title)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(description)
.fontSize(16)
.fontColor('#666666')
.textAlign(TextAlign.Center)
.lineHeight(24)
}
Column({ space: 10 }) {
this.buildSettingItem('🔧', '基础设置', '配置基本功能选项')
this.buildSettingItem('🎨', '界面设置', '自定义界面外观')
this.buildSettingItem('🔔', '通知设置', '管理通知提醒')
this.buildSettingItem('🔒', '安全设置', '保护账户安全')
}
}
.width('100%')
.height('100%')
.padding(30)
.justifyContent(FlexAlign.Center)
}
@Builder
buildCardContent(title: string, icon: string, color: string) {
Column({ space: 20 }) {
Text(icon)
.fontSize(48)
.fontColor(color)
Text(title)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text('这里是' + title + '的详细内容展示区域')
.fontSize(16)
.fontColor('#666666')
.textAlign(TextAlign.Center)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#FAFAFA')
}
@Builder
buildDynamicTabContent(item: TabItemData, index: number) {
Column({ space: 20 }) {
Text(item.icon)
.fontSize(40)
Text(item.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(`这是第${index + 1}个动态页签的内容`)
.fontSize(16)
.fontColor('#666666')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
@Builder
buildFeatureCard(icon: string, title: string, subtitle: string) {
Column({ space: 10 }) {
Text(icon)
.fontSize(32)
Text(title)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(subtitle)
.fontSize(12)
.fontColor('#999999')
.textAlign(TextAlign.Center)
}
.width('100%')
.height('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.shadow({
radius: 5,
color: '#20000000',
offsetX: 0,
offsetY: 2
})
}
@Builder
buildNewsItem(news: NewsItemData) {
Row({ space: 15 }) {
Text('📰')
.fontSize(24)
.width(40)
.textAlign(TextAlign.Center)
Column({ space: 8 }) {
Text(news.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(news.summary)
.fontSize(14)
.fontColor('#666666')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 10 }) {
Text(news.category)
.fontSize(12)
.fontColor('#3498DB')
.backgroundColor('#E3F2FD')
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
.borderRadius(10)
Text(news.time)
.fontSize(12)
.fontColor('#999999')
}
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.width('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({
radius: 5,
color: '#20000000',
offsetX: 0,
offsetY: 2
})
}
@Builder
buildSettingItem(icon: string, title: string, subtitle: string) {
Row({ space: 15 }) {
Text(icon)
.fontSize(24)
.width(40)
.textAlign(TextAlign.Center)
Column({ space: 5 }) {
Text(title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(subtitle)
.fontSize(14)
.fontColor('#666666')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('>')
.fontSize(16)
.fontColor('#C0C0C0')
}
.width('100%')
.height(60)
.padding({ left: 15, right: 15 })
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({
radius: 5,
color: '#20000000',
offsetX: 0,
offsetY: 2
})
}
addTab() {
const newTab: TabItemData = {
id: this.tabItems.length + 1,
title: `页签${this.tabItems.length + 1}`,
icon: ['🎯', '⭐', '🔥', '💎', '🚀'][Math.floor(Math.random() * 5)],
content: `这是新添加的页签内容`
}
this.tabItems.push(newTab)
}
removeTab(index: number) {
if (this.tabItems.length > 1) {
this.tabItems.splice(index, 1)
if (this.currentIndex >= this.tabItems.length) {
this.currentIndex = this.tabItems.length - 1
}
}
}
}// module.json5 配置
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background"
}
]
}
}
运行后的界面将展示:
通过这个Demo,我们学习了:
Tabs组件作为现代移动应用中最重要的导航组件之一,掌握其各种用法对于创建优秀的用户体验至关重要。从简单的底部导航到复杂的多级页签,每一个细节都体现着应用的专业性和用户友好性。
希望这个示例能够帮助到正在学习HarmonyOS开发的你!下一期我们将探索更多有趣的UI组件,敬请期待!如果你有任何问题或建议,欢迎在评论区留言交流。
🌟 如果这篇文章对你有帮助,请点赞支持!🌟
让我们一起在HarmonyOS的世界里创造更多可能!
© 2025 坚果派·红目香薰 | 用心分享,用技术创造价值