概述
Tabs组件是一个页签导航组件,我们常见的手机最底部的导航按钮可以使用Tab组件实现。其中一个页签对应下图中微信、通讯录、发现、我的其中一个页面。
Tabs组件内只允许使用子组件TabContent,每一个TabContent对应于一个页签。下面我们直接进入主题,使用Tabs和TabContent实现一个页签导航。
对于使用Tabs切换页签的界面可以分为两个部分——内容部分TabContent和切换栏TabBar部分,TabContent为对应显示的主要内容。而tabBar则是通过给TabContent设置tabBar属性进行设置,用来进行切换时显示的内容。
Tabs组件的基础使用
declare class TabContentAttribute extends CommonMethod<TabContentAttribute> {
tabBar(value: string | Resource | CustomBuilder | {
icon?: string | Resource;
text?: string | Resource;
}): TabContentAttribute;
}
可以查看TabContent的属性查看tabBar支持传入的参数类型,可以发现其可以传入string和icon等资源文件以及一个不认识的CustomBuilder,这里我们首先使用最简单的string类型来进行尝试使用。
@Entry
@Component
struct TestPage {
build() {
Tabs() {
TabContent() {
Text("首页的内容").fontSize(30)
}.tabBar("首页")
TabContent() {
Text("推荐的内容").fontSize(30)
}.tabBar("推荐")
TabContent() {
Text("发现的内容").fontSize(30)
}.tabBar("发现")
TabContent() {
Text("我的内容").fontSize(30)
}.tabBar("我的")
}
}
}
可以发现,就这几行简单的代码已经可以实现页签的切换效果了。但我们发现这个TabBar的位置在屏幕顶上,我们期望其在最下方屏幕底部。
通过查看Tabs组件的参数,发现一个参数barPosition,有点类似设置tabBar的位置
interface TabsInterface {
(value?: {
barPosition?: BarPosition;
index?: number;
controller?: TabsController;
}): TabsAttribute;
}
declare enum BarPosition {
Start,
End
}
其参数类型为BarPosition,可以设置的参数为Start和End,默认设置为Start,那修改为End后,可以发现可以达到预期的效果,tabBar处于屏幕底部了。
对于tabBar,如果要设置宽高,可以在Tabs组件上设置.barWidth("100%").barHeight(76)来设置其宽高,具体的参数由实际情况设置。
通常我们一个页签的内容不会写在一个页面中,会采用组件化的形式传入
TabContent中。
首先我们创建四个组件分别为HomePage、RecommendationPage、FoundPage、ProfilePage放在view文件夹中,对应我们要切换的四个页签,分别设置为如下内容。
@Component
export struct HomePage {
build() {
Row() {
Column() {
Text("首页的内容").fontSize(40)
}.height("100%")
}.width("100%")
}
}
@Component
export struct RecommendationPage {
build() {
Row() {
Column() {
Text("推荐的内容").fontSize(40)
}.height("100%")
}.width("100%")
}
}
@Component
export struct FoundPage {
build() {
Row() {
Column() {
Text("寻找的内容").fontSize(40)
}.height("100%")
}.width("100%")
}
}
@Component
export struct ProfilePage {
build() {
Row() {
Column() {
Text("我的内容").fontSize(40)
}.height("100%")
}.width("100%")
}
}
那么分离后的Main页面的内容就变成了,效果还是和之前的内容一样,只不过我让显示的页面文字居中了。
import { FoundPage } from '../View/FoundPage';
import { RecomedationPage } from '../View/RecomedationPage';
import { ProfilePage } from '../View/ProfilePage';
import { HomePage } from '../View/HomePage';
@Entry
@Component
struct MainApp {
build() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
HomePage()
}.tabBar("首页")
TabContent() {
RecomedationPage()
}.tabBar("推荐")
TabContent() {
FoundPage()
}.tabBar("寻找")
TabContent() {
ProfilePage()
}.tabBar("我的")
}
.barWidth("100%")
.barHeight(86)
}
}
页面内容暂时先不管,但我感觉我们的切换栏有点太简陋了,因为一般切换栏都会使用图标或者图片,让页面更好看。之前也介绍过,tabBar可以通过string、Resource或者CustomBuilder传入参数,icon的用法和之前的string一样,这里我们使用另外一种常用的方式,CustomBuilder来进行实现,并学习一些@Builder的知识。
declare class TabContentAttribute extends CommonMethod<TabContentAttribute> {
tabBar(value: string | Resource | CustomBuilder | {
icon?: string | Resource;
text?: string | Resource;
}): TabContentAttribute;
}
tabBar的特殊设置实现活跃页签状态高亮
CustomBuilder其实就是一个参数类型的组件,但这个组件需要是被@Builder装修器装饰的一个函数组件。意思其实就是写一个小组件并使用@Builder装饰,作为参数传入tabBar中,显示在切换栏中。这里我准备了四个图片。
ImageSrc:Array<string> = ["/asserts/TabImg/HomeImg.png",
"/asserts/TabImg/RecomedationImg.png",
"/asserts/TabImg/FoundImg.png",
"/asserts/TabImg/ProfileImg.png"]
tabContent:Array<string> = ["首页","推荐","发现","我的"]
并且使用index来从数组中取出对应的src路径,这就是我们所使用的tabBar的参数。
@Builder TabBuilder(index: number) {
Column() {
Image(this.ImageSrc[index])
.objectFit(ImageFit.Contain)
.width(50)
.height(50)
Text(this.tabContent[index]).fontSize(14)
}.width('100%')
}
在MainApp中,以this.TabBuilder(index)的方式对tabBar的参数传入其对应的index,按照顺序分别为0,1,2,3。
@Entry
@Component
struct MainApp {
// 对应的图片路径数组和文字内容数组
...
@Builder TabBuilder(index: number) {
Column() {
Image(this.currentIndex == index?this.SelectImgSrc[index]:this.ImageSrc[index])
.objectFit(ImageFit.Contain)
.width(50)
.height(50)
Text(this.tabContent[index]).fontSize(14)
}.width('100%')
}
build() {
Tabs({ barPosition: BarPosition.End}) {
TabContent() {
HomePage()
}.tabBar(this.TabBuilder(0))
TabContent() {
RecomedationPage()
}.tabBar(this.TabBuilder(1))
TabContent() {
FoundPage()
}.tabBar(this.TabBuilder(2))
TabContent() {
ProfilePage()
}.tabBar(this.TabBuilder(3))
}.onChange((index: number) => {
this.currentIndex = index
})
.barWidth("100%")
.barHeight(76)
}
}
但是我们发现,点击的时候确实发生了内容的切换,但是tabBar中却没有感知,这是由于我们没有对选中状态下的tabBar做特殊处理。一般文字我们会使用不同颜色来表示,而图片则使用不同颜色的图片进行特殊表示,所以对于当前点击的页签还需要做特殊处理,这里我准备了另一组图片,用来提示当前活跃的页面。
SelectImgSrc:Array<string> = ["/asserts/TabImg/HomeSelectImg.png",
"/asserts/TabImg/RecomedationSelectImg.png",
"/asserts/TabImg/FoundSelectImg.png",
"/asserts/TabImg/ProfileSelectImg.png"]
由于需要记录当前是那一组处于活跃状态,所以需要定义一个currentIndex来标志当前活跃的页签。
@State currentIndex: number = 0
而我们的TabBuilder也需要进行相应的修改,要使currentIndex对应的tabBar使用“高亮”的图片也就是其src路径要取自SelectImgSrc中,而其他处于未选中状态的TabBar则取自ImageSrc中。这里就是一个简单的判断
if(index == current) {
src = SelectImgSrc[index]
}else {
src = ImageSrc[index]
}//伪代码
那么再简单点就是使用三目运算符,currentIndex == index ? SelectImgSrc[index]:Image[index],运用到需要传入Src对应的位置如下。
@Builder TabBuilder(index: number) {
Column() {
Image(this.currentIndex == index?this.SelectImgSrc[index]:this.ImageSrc[index])
.objectFit(ImageFit.Contain)
.width(50)
.height(50)
Text(this.tabContent[index]).fontSize(14)
}.width('100%')
}
最终的效果如下,这样我们就简单实现了一个页签导航的效果,具体每一页的内容按照需求完善即可。
参考链接:
更多的Tabs组件详细用法参考:gitee.com/openharmony…