前言:最近做开发任务的时候,想把Tabs自定义了,并且动态配置TabContent里面的内容,不是写死一样的,这个问题困扰了很长时间,试过**@BuilderParam**(类似于vue的插槽)传组件方式的,但是**@BuilderParam只能传一个,我想要传递的是一个数组,扒了很多Api最后找到了WrappedBuilder[]**这种方式。
废话不多说,直接上代码,因为大部分的学习者都是先看代码,作为最直接的切入点,先做分解解释,最后附完整代码,以及示例用法。
import { Log4a,InitializeAllLoggers } from 'winloglib';
import {BlockController} from '../model/BlockController'
Log4a自定义的日志打印,主要是为了日志输出到本地的功能
BlockController:回调类,主要是一些闭包函数,用作父子组件之间的回调事件处理,以及传值功能
正文开始
@Builder
function MyBuilder() {}
自定义全局builder,WrappedBuilder使用的时候需要初始化,所以初始化为空
tabController: TabsController = new TabsController()
titleSelectColor: ResourceColor = '#00EB01';//选中高亮颜色
titleNormalColor: ResourceColor = '#182431';//字体默认颜色
dividerColor: ResourceColor = '#00EB01';//下划线颜色
dividerStrokeWidth: number | string = 2;//下划线高度
titleFontSize: number | string | Resource = 16; //字体默认大小
titleFontWeight: number | string | FontWeight = '400';//字体默认Weight
titleSelectFontWeight: number | string | FontWeight = '500';//字体默认Weight
titleLineHeight: number | string | Resource = '22';//默认高度
tabBarHeight: Length = 60;//默认高度
tabBarWidth: Length = '100%';//默认宽度
durationTime: number = 400;//默认动画时间
@State currentIndex: number = 0;
@Require titleNameList: string[] = []; //标题列表
@Require componentList: WrappedBuilder<[BlockController]>[] = [wrapBuilder(MyBuilder)];
@Builder FunABuilder0() {}
// 接受外部传入的AttributeModifier类实例
@Prop modifier: AttributeModifier<TabsAttribute> | null = null;
private block: BlockController = new BlockController();
private log4a: Log4a = new Log4a();
自定义组件需要的参数,这中自定义方法是鸿蒙不提倡的,因为需要太多的参数传递,这是我之前的自定义方案,还没来得及改动。
鸿蒙系统希望用AttributeModifier的方式自定义组件,我后面用这种方案实现自定义。本期的重点是实现TabContent的动态配置。
@Require titleNameList: string[] = []; //标题列表
这是tabs的标题列表数组,这个没什么解释的。
@Require componentList: WrappedBuilder<[BlockController]>[] = [wrapBuilder(MyBuilder)];
这个是关键,这个就是自定义的传递组件列表,接收组件列表的参数,需要WrappedBuilder包裹,<[BlockController]>这个是参数类型,是需要向你自定义组件传值,或者传递回调的时候用的,我这里面只需要传递回调方法,所以只有这一个类型,多参数凡是<[BlockController,string,number,object]>,当然你可以用自定义的数据类型,model类型。 这个解释很详细了,你可以看下官方文档对WrappedBuilder的解释以及示例。
declare function wrapBuilder< Args extends Object[]>(builder: (...args: Args) => void): WrappedBuilder;
// 组件生命周期
aboutToAppear() {
console.info('WinTabBarComponents aboutToAppear');
// InitializeAllLoggers();
this.log4a.info('WinTabBarComponents aboutToAppear');
this.block.toggleTabBlock = this.clickBlock;
}
//切换路线
changeRouteBlock: (param: string) => void = (param: string) => {
this.block.jumpNextVCBlock(param);
};
clickBlock: () => void = () => {
console.info('切换路线完成action:');
this.tabController.changeIndex(1);
}
changeTabIndex(index: number) {
this.block.changeTabIndexBlock(index);
}
这是我项目中需要对父组件回调事件做的处理,你可以自定义自己的回调事件,
//构造函数
@Builder tabBuilder(index: number,name?: string){
Column() {
Text(this.titleNameList[index])
.fontColor(this.currentIndex === index ? this.titleSelectColor : this.titleNormalColor)
.fontSize(this.titleFontSize)
.fontWeight(this.currentIndex === index ? this.titleFontWeight : this.titleSelectFontWeight)
.lineHeight(this.titleLineHeight)
.margin({ top: 7, bottom: 7 })
Divider()
.strokeWidth(this.dividerStrokeWidth)
.color(this.dividerColor)
.opacity(this.currentIndex === index ? 1 : 0)
}.width('100%')
}
这是Tabs的标题栏的构造函数,这个很简单,没什么解释的。
build() {
Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.tabController }) {
ForEach(this.componentList, (item: WrappedBuilder<[BlockController]>,index: number) => {
TabContent() {
item.builder(this.block)
}.tabBar(this.tabBuilder(index))
}, (item: WrappedBuilder<[BlockController]>) => JSON.stringify(item))
}
.vertical(false)
.barMode(BarMode.Fixed)
.barWidth(this.tabBarWidth)
.barHeight(this.tabBarHeight)
.animationDuration(this.durationTime)
.onChange((index: number) => {
this.currentIndex = index;
this.changeTabIndex(index)
})
}
这里就是实现TabContent的动态配置内部实现方法,ForEach循环的item的类型是WrappedBuilder<[BlockController]> ,这个参数是自己定义的,你可以传递多参数的**,item.builder(this.block)**就是你传参数给自定义组件的参数。
请看父组件的调用方法以及示例:
@Builder
function componentMineRoutePageBuilder(block: BlockController){
TodayVisitRoutePage({blockController:block}).padding({bottom:CommonConstants.TAB_PAD_BOTTOM});
}
@Builder
function componentTodayVisitStorePage(block: BlockController){
TodayVisitStorePage().padding({bottom:CommonConstants.TAB_PAD_BOTTOM});
}
@Builder
function componentMapPageBuilder(block: BlockController) {
Text('售点地图')
}
const builderArr: WrappedBuilder<[BlockController]>[] = [ wrapBuilder(componentMineRoutePageBuilder), wrapBuilder(componentTodayVisitStorePage), wrapBuilder(componentMapPageBuilder), wrapBuilder(componentMapPageBuilder) ];
这个是需要全局定义的@Builder方法,不能定义在struct里面的,需要全局定义。 TodayVisitRoutePage,TodayVisitStorePage都是我自定义的组件,这样就动态实现TabContent的配置,不再是一样的写法,也可以传递参数,以及回调方法。
示例:
@Entry
@Component
export struct StoreListPage{
private blockRef = new BlockController();
@State tabNameList: string[] = [ '我的路线', '今日路线(0)', '门店列表(90)', '售点地图' ];
build() {
Column(){
WinTabBarComponents({
titleNameList: this.tabNameList,
componentList: builderArr,
block: this.blockRef,
});
}
}
}
使用方法也很简单,传递你自定义的WrappedBuilder数组,自定义的Tabs会根据你传递的数组数量创建相同数量的TabContent了。
完整自定义的代码如下
import { Log4a,InitializeAllLoggers } from 'winloglib';
import {BlockController} from '../model/BlockController'
@Builder
function MyBuilder() {}
@Component
export struct WinTabBarComponents{
tabController: TabsController = new TabsController()
titleSelectColor: ResourceColor = '#00EB01';//选中高亮颜色
titleNormalColor: ResourceColor = '#182431';//字体默认颜色
dividerColor: ResourceColor = '#00EB01';//下划线颜色
dividerStrokeWidth: number | string = 2;//下划线高度
titleFontSize: number | string | Resource = 16; //字体默认大小
titleFontWeight: number | string | FontWeight = '400';//字体默认Weight
titleSelectFontWeight: number | string | FontWeight = '500';//字体默认Weight
titleLineHeight: number | string | Resource = '22';//默认高度
tabBarHeight: Length = 60;//默认高度
tabBarWidth: Length = '100%';//默认宽度
durationTime: number = 400;//默认动画时间
@State currentIndex: number = 0;
@Require titleNameList: string[] = []; //标题列表
@Require componentList: WrappedBuilder<[BlockController]>[] = [wrapBuilder(MyBuilder)];
@Builder FunABuilder0() {}
// 接受外部传入的AttributeModifier类实例
@Prop modifier: AttributeModifier<TabsAttribute> | null = null;
private block: BlockController = new BlockController();
private log4a: Log4a = new Log4a();
// 组件生命周期
aboutToAppear() {
console.info('WinTabBarComponents aboutToAppear');
// InitializeAllLoggers();
this.log4a.info('WinTabBarComponents aboutToAppear');
this.block.toggleTabBlock = this.clickBlock;
}
//切换路线
changeRouteBlock: (param: string) => void = (param: string) => {
this.block.jumpNextVCBlock(param);
};
clickBlock: () => void = () => {
console.info('切换路线完成action:');
this.tabController.changeIndex(1);
}
changeTabIndex(index: number) {
this.block.changeTabIndexBlock(index);
}
//构造函数
@Builder tabBuilder(index: number,name?: string){
Column() {
Text(this.titleNameList[index])
.fontColor(this.currentIndex === index ? this.titleSelectColor : this.titleNormalColor)
.fontSize(this.titleFontSize)
.fontWeight(this.currentIndex === index ? this.titleFontWeight : this.titleSelectFontWeight)
.lineHeight(this.titleLineHeight)
.margin({ top: 7, bottom: 7 })
Divider()
.strokeWidth(this.dividerStrokeWidth)
.color(this.dividerColor)
.opacity(this.currentIndex === index ? 1 : 0)
}.width('100%')
}
build() {
Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.tabController }) {
ForEach(this.componentList, (item: WrappedBuilder<[BlockController]>,index: number) => {
TabContent() {
item.builder(this.block)
}.tabBar(this.tabBuilder(index))
}, (item: WrappedBuilder<[BlockController]>) => JSON.stringify(item))
}
.vertical(false)
.barMode(BarMode.Fixed)
.barWidth(this.tabBarWidth)
.barHeight(this.tabBarHeight)
.animationDuration(this.durationTime)
.onChange((index: number) => {
this.currentIndex = index;
this.changeTabIndex(index)
})
}
}
我们的UI如图所示
最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。
而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点
如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。
高清完整版请点击→《鸿蒙NEXT星河版开发学习文档》
针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。
《鸿蒙 (OpenHarmony)开发学习视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
《鸿蒙开发基础》
《鸿蒙开发进阶》
《鸿蒙开发实战》
获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》
总结
鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。
并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!