前言
对于做APP的UI,免不了会写大量的重复布局,重复UI页面。此时对于将重复的UI控件抽离出来封装为通用组件来进行优化很是重要。
本文重点分析鸿蒙几种UI处理上,如何抽离通用方法来进行UI的复用。重点对比@Style,@Extend, AttributeModifier, @Builder和 struct 这五种方法的区别和使用场景。
Styles装饰器
学过Android开发的小伙伴知道Android中有样式Style概念,我们定义好一个样式,那么就可以在各个组件中使用。从而保持每个组件有一样的属性。
同理,鸿蒙中也可以使用样式。比如我们UI上的按钮具有相同的宽,高,背景色和边角距。那么我们就可以定义一个Style,每次定义按钮时候,只需要将Style赋给按钮就可以实现该属性,从而避免重复代码的书写。
●代码说明
如图,在当前页面内定义一个方法,使用装饰器Styles修饰,修饰后,方法内部就可以直接通过 .属性 的方式来定义属性了。方法定义完后,下方button里可以直接使用该方法。虽然我这里方法命名为commonButton,但是实际上所有基础控件都可以使用该方法使用里边的属性,比如下方的Text组件。
●Style特点
1.对于定义的方法,无法使用export修饰。
这也就意味着,我们抽离的通用属性,只能在当前页面内的组件上使用,换个页面就不可以了,无法做到全局所有页面通用。
2.对于定义的方法,只能定义组件的通用属性。
比如宽高,背景色等。对于一些控件特有属性是无法定义的。比如Select组件的selectedOptionFont特有属性无法定义。
3.方法不支持传递入参。
意味着该样式无法做到动态修改,只要定义好就无法修改。比如定义好宽高为30,而某个组件宽要求为40,其他属性都不变,那这个方法也没法用了。
4.方法为组件通用属性,故所有组件都可以引用方法。
Extend装饰器
对于Styles装饰器的第2点限制,鸿蒙推出了解决方案,那就是使用@Extend装饰器。
Extend装饰器需要我们在使用时候指定定义哪个组件的属性,是专门抽离指定组件的。
●代码说明
Extend要求必须定义方法为在当前文件的全局定义,且也不能够export,同时定义时候需要指定是针对哪个控件。如图指定了控件Select,然后就可以指定Select的专有属性了。
●Extend特点
1.方法不支持export。
和Styles一样,无法真正做到为所有页面抽离出都可用的属性方法。
2.方法只能定义为当前页面内的全局方法。
一定程度上全局方法存在引用GlobalThis,具体副作用未知。
3.方法需要指定控件,其他控件无法使用,只能对专有控件做到了抽离
4.方法可以传入参。相比Styles, 可以在其他属性不变的情况下,只修改其中的部分属性。
AttributeModifier
对于上述两个装饰器都存在一个相同的限制,就是无法做到全局所有文件都可以公用。
AttributeModifier的出现可以变相的解决这个问题。AttributeModifier本意是用于自定义控件中设置属性用的。但是我们在这里也可以通过这个机制,来实现全局所有文件中控件均可通用的属性。
●代码说明
该Modifier只能针对专用控件,比如我要抽离一个通用的TextInput,那么我可以如上图所定义。
需要实现一个接口 AttributeModifier,接口泛型定义和我们想要给哪个控件使用有关,比如我们想给TextInput使用,那么泛型就是 TextInputAttribute,如果给Column使用,那么泛型就是ColumnAttribute,以此类推。
在该接口的实现方法中,定义控件的属性。
●布局中使用
●自定义属性
我们还可以自定义部分属性,只需要修改TextInputAttribute,例如我们想自定义字体大小。可以定义变量。
●使用
●AttributeModifier特点
1.可以全局给所有页面中的控件使用
2.可以自定义任何控件中的属性,包括特有属性
3.可以通过修改代码做成链式调用
4.该方法需要new对象,比较笨重,需要书写较多代码
@Builder
上述说的都是针对单独的控件,如果我们想抽离一个通用的布局呢?或者我们的控件就是固定的可以拿来到处使用。
比如我有一个Text,各种属性固定,只是文案不同,那么我使用上述几种都比较麻烦,需要书写较多代码。那么这个时候就可以使用builder了。
●代码
我们可以在任意需要展示该Text的地方使用,直接调用该方法,对应位置就可以显示出内容了。原理相当于是将方法内的控件代码放到了对应的位置上。
●使用
● @Builder特点
1.定义好方法后,需要拿Builder装饰器修饰,可以在任何一个页面内调用方法使用。
2.可以通过方法传递入参
3.无法通过方法拿到控件对象,只能在方法里操作控件属性
4.除了单一控件,还可以定义布局,布局中存在多个控件的情况
5.轻量级
6.方法即拿即用,代码量少
struct
有时候,我们可能页面中存在大量如下UI:
对于这种UI,我们完全可以抽离出为一个控件。然后我们页面需要展示的地方,直接调用该控件,设置标题,按钮文案等就可以简化了。
我们可能想到使用builder来定义,但是builder只能写纯UI代码,这里还涉及到用户输入的内容,如何在点击按钮时候传过去。所以builder就无法使用了,这个时候就可以用struct封装了。
●代码
@Component
export struct InputNumberItemWithButton {
label: string = "标题"
buttonClick: (v: number) => void = () => {
}
buttonLabel: string = "设置"
inputPlaceholder: string = "我是提示语"
inputId: string = this.label
parentWidth: string = '100%'
private value: number = 0
build() {
RelativeContainer() {
Text(this.label)
.attributeModifier(Modifier.textLabel())
.id('label1')
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
.margin({ left: 2 })
TextInput({ placeholder: this.inputPlaceholder })
.onChange((value: string) => {
this.value = Number.parseInt(value) ?? 0
})
.type(InputType.Number)
.id(this.inputId)
.height(30)
.placeholderFont({ size: 10 })
.fontSize(CommonStyle.INPUT_TEXT_SIZE)
.borderRadius(4)
.alignRules({
right: { anchor: 'button1', align: HorizontalAlign.Start },
left: { anchor: 'label1', align: HorizontalAlign.End },
top: { anchor: '__container__', align: VerticalAlign.Top },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
.margin({ left: 6, right: 6 })
Button(this.buttonLabel)
.attributeModifier(SuBaoSmallButtonModifier.create())
.onClick(() => {
this.buttonClick(this.value)
})
.id('button1')
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
top: { anchor: '__container__', align: VerticalAlign.Top },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
.margin({ right: 2 })
}
.width(this.parentWidth)
.height(40)
.padding({
left: 5,
right: 5,
top: 2,
bottom: 2
})
.borderRadius(4)
}
}
该struct中通过维护一个变量value 来保存用户输入的数字,然后在用户点击按钮时候传给点击事件方法,交给调用者调用。
●使用
点击设置按钮,点击事件触发,a直接赋值。
●struct特点
1.可以封装复杂组件,自定义组件
2.可以维护变量存储用户输入输出
3.可以所有页面全局使用
4.可以自定义属性
6.无法链式设置属性
对比各个使用场景
实际编程中,一般都是混合相互配合使用,没必要单独硬使用哪一个。
1.style可以用来定义一些通用属性,比如背景色,边角据等
2.Extend对于页面中一些特殊的控件,用的地方较多时候,可以抽离方法
3.AttributeModifier如果Extend无法满足,那么选择这个
4.Builder对于布局控件的属性变化不大,但是用的地方多时候使用,比如定义一个分割线。
5.struct涉及到用户输入输出时候,相关控件可以抽离封装,避免页面内上方定义太多变量,不好维护。
写在最后
有很多小伙伴不知道该从哪里开始学习鸿蒙开发技术?也不知道鸿蒙开发的知识点重点掌握的又有哪些?自学时频繁踩坑,导致浪费大量时间。结果还是一知半解。所以有一份实用的鸿蒙(HarmonyOS NEXT)全栈开发资料用来跟着学习是非常有必要的。