鸿蒙应用开发中,如何深入解析并演示Tabs标签导航组件?
摘要:【学习目标】 掌握 Tabs 组件核心定位、基础结构与使用规范,理解 TabContent 的布局限制 通过示例实现底部顶部侧边三种标准导航布局 掌握 barMode、scrollable、animationDuration 等常用属性
【学习目标】
掌握 Tabs 组件核心定位、基础结构与使用规范,理解 TabContent 的布局限制
通过示例实现底部/顶部/侧边三种标准导航布局
掌握 barMode、scrollable、animationDuration 等常用属性配置
学会自定义 TabBar、代码控制切换、切换拦截等进阶用法
一、本节工程目录结构(API20)
TabsDemo/
└── entry/
└── src/
└── main/
├── ets/
│ ├── components/ # 自定义公共组件
│ │ ├── CustomTabBar.ets
│ │ └── HomeComponent.ets
│ ├── pages/ # 主页面 + 示例页
│ │ ├── Index.ets 课程导航主页
│ │ ├── BasicTabs.ets 基础三方向导航示例
│ │ ├── BuilderTabs.ets @Builder 自定义TabBar
│ │ └── CustomTabs.ets 完全自定义异形TabBar
│ └── entryability/
│ └── EntryAbility.ets
└── resources/
└── base/
└── media/
二、Tabs 核心基础认知
2.1 什么是 Tabs 组件
Tabs 是鸿蒙中实现单页面多视图快速切换的导航组件,广泛用于应用主界面底部导航、内容分类顶部导航等场景,是 App 开发中最常用的导航容器之一。
2.2 组件结构
Tabs 由三部分组成,必须一一对应:
Tabs 容器:管理整体布局、动画、滑动、事件
TabBar:每个页面的导航标签
TabContent:每个标签对应的内容区域
2.3 使用规则
TabContent 不建议手动设置宽高,默认撑满 Tabs 剩余空间
TabContent 书写顺序就是页签索引顺序,从 0 开始
每个 TabContent 必须配置 .tabBar(),否则页签不显示
TabsController 可以手动控制选中哪个
onContentWillChange 可以提前拦截点击。
三、基础示例(pages/BasicTabs.ets)
@Entry
@Component
struct BasicTabs {
@State currentIndex: number = 0;
@State barPos: BarPosition = BarPosition.End;
@State isVertical: boolean = false;
// 1. 创建 Tabs 控制器(代码跳转用)
private tabsController: TabsController = new TabsController();
build() {
Column() {
Row({ space: 12 }) {
Button("底部导航").onClick(() => {
this.barPos = BarPosition.End;
this.isVertical = false;
})
Button("顶部导航").onClick(() => {
this.barPos = BarPosition.Start;
this.isVertical = false;
})
Button("侧边导航").onClick(() => {
this.barPos = BarPosition.Start;
this.isVertical = true;
})
}
.margin(12)
.justifyContent(FlexAlign.Center)
// 2. 绑定 controller
Tabs({ barPosition: this.barPos, index: this.currentIndex, controller: this.tabsController }) {
TabContent() {
Column() {
Text("首页内容").fontSize(30)
// 3. 按钮代码控制跳转
Button("跳转到发现").onClick(() => {
this.tabsController.changeIndex(2);
}).margin({top:10});
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.justifyContent(FlexAlign.Center)
}
.tabBar("首页")
TabContent() {
Column() {
Text("分类内容").fontSize(30)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.justifyContent(FlexAlign.Center)
}
.tabBar("分类")
TabContent() {
Column() {
Text("发现内容").fontSize(30)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.justifyContent(FlexAlign.Center)
}
.tabBar("发现")
TabContent() {
Column() {
Text("我的内容").fontSize(30)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.justifyContent(FlexAlign.Center)
}
.tabBar("我的")
}
.layoutWeight(1)
.vertical(this.isVertical)
.barWidth(this.isVertical ? 120 : '100%')
.barHeight(this.isVertical ? '100%' : 50)
.onChange(index => this.currentIndex = index)
// 4. 切换拦截(演示:禁止跳转到“我的”页面)
.onContentWillChange((from: number, to: number) => {
if (to === 3) {
console.log("拦截成功:无法进入我的页面!");
return false; // 返回 false 阻止跳转
}
return true;
})
}
.width('100%')
.height('100%')
}
}
运行效果
三种布局对应配置
布局
barPosition
vertical
典型场景
底部导航
BarPosition.End
false
应用主入口
顶部导航
BarPosition.Start
false
内容分类、资讯标签
侧边导航
BarPosition.Start
true
平板、横屏、管理后台
四、Tabs 常用属性配置
4.1 barMode:导航栏模式
BarMode.Fixed:均分宽度,不可滚动(默认)
BarMode.Scrollable:可横向滚动,适合多标签
4.2 scrollable:是否允许滑动切换
嵌套导航时建议关闭,避免滑动冲突。
4.3 animationDuration:切换动画时长
单位:毫秒,默认 300。
4.4 cachedMaxCount:页面缓存数量(API19+)
提升切换流畅度。
4.5 AnimationMode 切换动画模式
enum AnimationMode {
CONTENT_FIRST = 0, // 先加载页面内容,再执行切换动画
ACTION_FIRST = 1, // 先执行切换动画,再加载页面内容
NO_ANIMATION = 2, // 无任何动画,直接切换页面(本案例常用)
CONTENT_FIRST_WITH_JUMP = 3, // 先加载页面,无动画跳转,再执行动画
ACTION_FIRST_WITH_JUMP = 4 // 先无动画跳转,再执行动画
}
五、@Builder 自定义 TabBar(pages/BuilderTabs.ets)
@Entry
@Component
struct BuilderTabs {
@State currentIndex: number = 0;
@Builder
TabItem(title: string, index: number, nor: Resource, sel: Resource,) {
Column({ space: 4 }) {
Image(this.currentIndex === index ? sel : nor).size({ width: 24, height: 24 })
Text(title).fontSize(12).fontColor(this.currentIndex === index ? '#07C160' : '#999')
}
.width('100%')
.justifyContent(FlexAlign.Center)
}
build() {
Column() {
Tabs({ barPosition: BarPosition.End, index: this.currentIndex }) {
TabContent() {
Column() { Text("首页").fontSize(30) }.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
.tabBar(this.TabItem("首页", 0, $r('app.media.tab_wechat_normal'), $r('app.media.tab_wechat_selected')))
TabContent() {
Column() { Text("分类").fontSize(30) }.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
.tabBar(this.TabItem("分类", 1, $r('app.media.tab_contact_normal'), $r('app.media.tab_contact_selected')))
TabContent() {
Column() { Text("发现").fontSize(30) }.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
.tabBar(this.TabItem("发现", 2, $r('app.media.tab_find_normal'), $r('app.media.tab_find_selected')))
TabContent() {
Column() { Text("我的").fontSize(30) }.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
.tabBar(this.TabItem("我的", 3, $r('app.media.tab_mine_normal'), $r('app.media.tab_mine_selected')))
}
.scrollable(false)
.animationMode(AnimationMode.NO_ANIMATION)
.onChange(index => this.currentIndex = index)
}
.width('100%')
.height('100%')
}
}
运行效果
六、完全自定义TabBar
6.1 自定义导航组件(components/CustomTabBar.ets)
import { CustomTabBar } from '../components/CustomTabBar'
import { HomeComponent } from '../components/HomeComponent';
@Entry
@Component
struct CustomTabs {
@State currentIndex: number = 0;
build() {
Column() {
Tabs({ index: this.currentIndex }) {
TabContent() { HomeComponent() }
TabContent() { Text("发现").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
TabContent() { Text("发布").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
TabContent() { Text("消息").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
TabContent() { Text("我的").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
}
.scrollable(false)
.barHeight(0)
.layoutWeight(1)
.barMode(BarMode.Fixed)
.animationMode(AnimationMode.NO_ANIMATION)
CustomTabBar({
currentIndex: $currentIndex,
tabList: [
{ title: "首页" },
{ title: "发现" },
{ normalImg: $r('app.media.ic_add'), selectedImg: $r('app.media.ic_add') },
{ title: "消息" },
{ title: "我的" }
],
onTabClick: (idx) => { this.currentIndex = idx }
})
}
.width('100%')
.height('100%')
}
}
6.2 首页顶部导航组件(components/HomeComponent.ets)
@Component
export struct HomeComponent {
build() {
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
Column() {
Text("关注页面").fontSize(30)
}.backgroundColor(Color.Pink)
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.tabBar({text:"关注"})
TabContent() {
Column() {
Text("发现页面").fontSize(30)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.tabBar("发现")
TabContent() {
Column() {
Text("北京页面").fontSize(30)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.tabBar("北京")
}
.barMode(BarMode.Fixed)
.barHeight(44)
.backgroundColor($r('sys.color.ohos_id_list_background_color'))
.cachedMaxCount(3,TabsCacheMode.CACHE_LATEST_SWITCHED)
.width('100%')
}
}
6.3 父页面(pages/CustomTabs.ets)
import { CustomTabBar } from '../components/CustomTabBar'
import { HomeComponent } from '../components/HomeComponent';
@Entry
@Component
struct CustomTabs {
@State currentIndex: number = 0;
build() {
Column() {
Tabs({ index: this.currentIndex }) {
TabContent() { HomeComponent() }
TabContent() { Text("发现").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
TabContent() { Text("发布").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
TabContent() { Text("消息").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
TabContent() { Text("我的").fontSize(30).width('100%').height('100%').textAlign(TextAlign.Center) }
}
.scrollable(false)
.barHeight(0) // 自定义tabar后 需要吧系统的设置为0,否则会占位
.layoutWeight(1)
.barMode(BarMode.Fixed)
.animationMode(AnimationMode.NO_ANIMATION)
CustomTabBar({
currentIndex: $currentIndex,
tabList: [
{ title: "首页" },
{ title: "发现" },
{ normalImg: $r('app.media.ic_add'), selectedImg: $r('app.media.ic_add') },
{ title: "消息" },
{ title: "我的" }
],
onTabClick: (idx) => { this.currentIndex = idx }
})
}
.width('100%')
.height('100%')
}
}
运行效果
七、知识点总结
Tabs = 容器 + TabBar + TabContent
支持底部/顶部/侧边三种导航模式
可配置滚动、动画、缓存、指示器等
@Builder 可快速实现统一样式 TabBar
TabsController 实现代码控制跳转
onContentWillChange 实现页面切换拦截
完全自定义组件可实现异形突出效果
八、代码仓库
工程名称:TabsDemo
仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
九、下节预告
下一节我们将学习媒体查询 @ohos.mediaquery 响应式布局,实现横竖屏、深浅色模式与多设备自适应,并封装通用媒体查询工具类。
