ArkUI 中 Tabs 的使用
当页面信息较多时,为了让用户能够聚焦于当前显示的内容,需要对页面内容进行分类,提高页面空间利用率。Tabs 组件可以在一个页面内快速实现视图内容的切换,一方面提升查找信息的效率,另一方面精简用户单次获取到的信息量。
基础用法
一、 容器的基本写法
- Tabs 容器内仅可以有 TabContent 子组件
- 每个 TabContent 容器都代表一个页面, 且其内部仅可有一个子组件
- TabContent 组件的 tabBar() 方法为其所对应的标签页的标题
// 选项卡容器写法
Tabs(){
// 选项卡 1
TabContent(){
Text("Page_1")
}
.tabBar("Page_1")
// 选项卡 2
TabContent(){
Text("Page_2")
}
.tabBar("Page_2")
}
二、设置标签栏的位置
底部导航是应用中最常见的一种导航方式。底部导航位于应用一级页面的底部,用户打开应用,能够分清整个应用的功能分类,以及页签对应的内容,并且其位于底部更加方便用户单手操作。底部导航一般作为应用的主导航形式存在,其作用是将用户关心的内容按照功能进行分类,迎合用户使用习惯,方便在不同模块间的内容切换。
-
使用 BarPositon( BarPosition.End ) 方法将标签栏的位置设置在页面的下方。
Tabs(){ // 选项卡 1 TabContent(){ Text("Page_1") } .tabBar("Page_1") // 选项卡 2 TabContent(){ Text("Page_2") } .tabBar("Page_2") } // 默认为 BarPosition.Start 页面标签显示在上方 .barPosition( BarPosition.End )
侧边导航是应用较为少见的一种导航模式,更多适用于横屏界面,用于对应用进行导航操作,由于用户的视觉习惯是从左到右,侧边导航栏默认为左侧侧边栏。
-
使用 vertical ( true ) 方法将标签栏的位置设置在页面的左方
Tabs(){ // 选项卡 1 TabContent(){ Text("Page_1") } .tabBar("Page_1") // 选项卡 2 TabContent(){ Text("Page_2") } .tabBar("Page_2") } // 默认为 false 页面标签显示在左方 .vertical( true )!!! 配合 BarPositon.End 可以实现将标签栏的位置设置在页面的右方
三、关闭滑动切换页签
默认情况下,导航栏都支持滑动切换,在一些内容信息量需要进行多级分类的页面,如支持底部导航+顶部导航组合的情况下,底部导航栏的滑动效果与顶部导航出现冲突,此时需要限制底部导航的滑动,避免引起不好的用户体验。
- 对外面的 Tabs 用上 scrollable( false ) 方法限制页签的切换
Tabs(){
TabContent(){
// 嵌套 Tabs
Tabs(){
TabContent(){
Text("Page 1-1")
}
.tabBar("Page 1-1")
TabContent(){
Text("Page 1-2")
}
.tabBar("Page 1-2")
}
}
.tabBar("Page_1")
TabContent(){
}
.tabBar("Page_2")
}
.barPosition ( BarPosition.End )
// 设置为 false 时禁用滑动切换页签
.scrollable ( false )
四、页签栏相关的设置
-
设置默认显示的页签
默认在进入时会显示第一个页面 ( 索引值为 0 ),在某些场景下需要默认显示其他的页面此时可以设置容器参数 index 属性使进入页面时显示指定索引值对应的页面 ( 索引值从 0 开始 )。
在传入的值为状态变量时如果后续改变了该状态变量的值,那么 tabs 显示的页面也会发生相应的变化 ( 在值的大小超出页面的数量时会默认显示索引值为0的页面 )
Tabs( {index: 1} ){} -
设置页签的显示方式
在默认模式下页签会平方标题栏的空间,所以在页签过多时无法正常显示页签内容 ( 见 图 )
此时使用 barMode () 方法使页签可以滚动显示。 参数 BarMod 为枚举类型,共有两个值: Fixed 表示平均分配空间、Scrollable 表示页签可以滚动显示;
Tabs(){ TabContent().tabBar("TabBarName") TabContent().tabBar("TabBarName") TabContent().tabBar("TabBarName") TabContent().tabBar("TabBarName") TabContent().tabBar("TabBarName") ... } .barMod ( BarMod.Scrollable )
自定义页签样式
对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会组合文字以及对应语义图标表示页签内容,这种情况下,需要自定义导航页签的样式。
一、自定义 tabBar 样式
在 TabContent 对应tabBar属性中传入自定义函数组件,并传递相应的参数。页签的样式就会变成所对应的构建函数。
- Builder 函数参数:
- title: 页签对应的文字
- path:页签所对应的图标
- index: 表示页签对应的索引值 ( 后面有用 )
Tabs(){
TabContent().tabBar( this.MyBar("Page1", "/image/img_1.jpg", 0) )
TabContent().tabBar( this.MyBar("Page2", "/image/img_2.jpg", 1) )
}
// title: 页签对应的文字
// path:页签所对应的图标
// index: 表示页签对应的索引值 ( 后面有用 )
@Builder MyBuilder(title: string, path: string, index: number){
Column(){
Image(path)
.width(20)
Text(title)
}
}
方法一: 事件监听
- 为了区别选中状态和非选中状态的页签需要实现相应的样式。
- 当页面发生变化时将索引值 index 传入 currentIndex,由于他是状态变量所以 currentIndex 的值的变化会令其他用到该变量的组件发生变化。
- 在 Builder 内部使用三元运算符判断当前页签的索引值 与 局部定义的 状态变量 curtentIndex 的值是否相等 ( 判断当前切换到的页面是否对应自身 ) 使当前被选中的页签的颜色与其他非选中组件的有所区别。
// 需写在 build() 函数外
@State currentIndex: number = 0
...
Tabs(){
TabContent().tabBar( this.MyBar("Page1", "/image/img_1.jpg", 0) )
TabContent().tabBar( this.MyBar("Page2", "/image/img_2.jpg", 1) )
}
// 注册事件监听 Tabs 容器的页面变化,在在进入新页面时执行内部代码并将新页面的索引值传入 index
.onchange ((index: number) => {
this.currentIndex = number
})
@Builder MyBuilder(title: string, path: string, index: number){
// currentIndex 只会等于一个值,所以只会有一个被选中的组件
// 三元运算符为 真: 表示当前页签的索引值等于当前显示的页面,让组件的颜色变成红色
// 为 假: 表示当前页签的索引值不等于当前显示的页面,让组件的颜色变成灰色
Column(){
// 只有使用 SVG 类型的图片才能改变图片的填充图片
Image(path)
.fillColor(this.currentIndex == index ? Color.Red : Color.Gray )
.width(20)
Text(title)
.fontColor(this.currentIndex == index ? Color.Red : Color.Gray )
}
}
方法二: 双向绑定
- 为了区别选中状态和非选中状态的页签需要实现相应的样式。
- Tabs 中的 index 值为当前所显示的页面的索引值。在没有进行双向绑定时仅能使 Tabs 页面显示对应索引值的页面,在使用滑动或点击的方式切换页面时状态变量无法获得对应的值。而使用双向绑定后就可以获取到当前显示页面的值
- 此时使用双向绑定的 currentIndex 就可以替代 Onchange()
// 需写在 build() 函数外
@State currentIndex: number = 0
...
// 将状态变量 currentIndex 与Tabs 的 index 参数进行双向绑定
Tabs({ index: $$currentIndex }){
TabContent().tabBar( this.MyBar("Page1", "/image/img_1.jpg", 0) )
TabContent().tabBar( this.MyBar("Page2", "/image/img_2.jpg", 1) )
}
@Builder MyBuilder(title: string, path: string, index: number){
// currentIndex 只会等于一个值,所以只会有一个被选中的组件
// 三元运算符为 真: 表示当前页签的索引值等于当前显示的页面,让组件的颜色变成红色
// 为 假: 表示当前页签的索引值不等于当前显示的页面,让组件的颜色变成灰色
Column(){
// 只有使用 SVG 类型的图片才能改变图片的填充图片
Image(path)
.fillColor(this.currentIndex == index ? Color.Red : Color.Gray )
.width(20)
Text(title)
.fontColor(this.currentIndex == index ? Color.Red : Color.Gray )
}
}
二、自定义页签
对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会组合文字以及对应语义图标表示页签内容,这种情况下,需要自定义导航页签的样式。而内部的 tabBar 限制较多为实现下图效果只能摈弃 Tabs 的 tabBar,使用自己写的 tabbar 然后绑定 Tabs 容器
方法一:注册事件
- 创建 Tabs 时内部的 TabContent 页面容器不要设置 tabBar 页签,且使用 .barHeight( 0 ) 将页签栏的高度设置为 0 为后续自定义的页签栏留出位置。
- 创建 Tabs 控制器对象 TabController,并将其与 Tabs 进行绑定。
- 设置自定义页签栏 ( 内部样式可以完全自定义 ),页签的样式会随着 currentIndex 的值变化而变化
- 在 Tabs 上注册 onChange() 事件监听页面的变化并将值赋给 currentIndex。 ↑↑↑↑↑↑↑↑↑↑↑↑ ( 页面的变化带动自定义页签的变化 )
- 在自定义页签上注册 onClick() 事件,在页签被点击时借助 Tabs 控制器对象 tabscontroller 的 changeIndex( 索引值 )方法改变当前正在显示的页面。 ( 点击页签切换页面 ) !!!! 需要将当前的索引值赋值给 currentIndex 不然页签的样式无法更新
@Entry
@Component
struct Test2 {
@State CureentIndex: number = 0
// 创建 Tabs 容器的控制器对象
tabcontroller: TabsController = new TabsController()
build() {
Column(){
// 将 Tabs 控制器对象绑定 Tabs 容器
Tabs({controller: this.tabcontroller}){
TabContent(){ Text("Page_1") }
TabContent(){ Text("Page_2") }
TabContent(){ Text("Page_3") }
}
.barHeight(0)
.height("80%")
// 监听 Tabs 页面的变化, 以带动页签的变化
.onChange((index: number) => {
this.CureentIndex = index
})
Row({space: 50}){
Text("Page_1")
// 注册点击事件,借助 this.tabcontroller.changeIndex(0) 切换显示页面
.onClick(() => {
// !!!! 需要将当前的索引值赋值给 currentIndex 不然页签的样式无法更新
this.currentIndex = 0
this.tabcontroller.changeIndex(0)
})
.fontColor(this.CureentIndex == 0 ? Color.Red : Color.Black)
Text("Page_2")
.onClick(() => {
this.currentIndex = 1
this.tabcontroller.changeIndex(1)
})
.fontColor(this.CureentIndex == 0 ? Color.Red : Color.Black)
Text("Page_3")
.onClick(() => {
this.currentIndex = 2
this.tabcontroller.changeIndex(2)
})
.fontColor(this.CureentIndex == 0 ? Color.Red : Color.Black)
}
.width("100%")
}
}
}
方法二: 双向绑定
- 创建 Tabs 时内部的 TabContent 页面容器不要设置 tabBar 页签,且使用 .barHeight( 0 ) 将页签栏的高度设置为 0 为后续自定义的页签栏留出位置。
- 设置自定义页签栏 ( 内部样式可以完全自定义 ),在 Text 上绑定点击事件令 Tabs 显示文本对应的索引值的页面。
- 将 Tabs 的 index 参数和 CurrentIndex 进行双向绑定。当使用滑动或切换改变页面时 currentIndex 的值也会发生变化,当状态变量变化时 Text 的样式会重新渲染。
@Entry
@Component
struct Test2 {
@State CureentIndex: number = 0
build() {
Column(){
Tabs({index: $$this.CureentIndex}){
TabContent(){ Text("Page_1") }
TabContent(){ Text("Page_2") }
TabContent(){ Text("Page_3") }
}
.barHeight(0)
.height("80%")
// 自定义 TabBar, 内部样式可以完全自定义
Row({space: 50}) {
Text("Page_1")
// 点击该文本时将 Tabs 切换为索引值为 0 的页面
.onClick(() => { this.CureentIndex = 0})
// 当状态变量发生变化时判断当前显示的页面与自身的索引是否一样,以显示不同的文字样式
.fontColor(this.CureentIndex == 0 ? Color.Red : Color.Black)
Text("Page_2")
.onClick(() => { this.CureentIndex = 1})
.fontColor(this.CureentIndex == 1 ? Color.Red : Color.Black)
Text("Page_3")
.onClick(() => { this.CureentIndex = 2})
.fontColor(this.CureentIndex == 2 ? Color.Red : Color.Black)
}
.width("100%")
}
}
}