注册
web

鸿蒙UI通用代码几种抽离方法

    对于做APP的UI,免不了会写大量的重复布局,重复UI页面。此时对于将重复的UI控件抽离出来封装为通用组件来进行优化很是重要。


    本文重点分析鸿蒙几种UI处理上,如何抽离通用方法来进行UI的复用。重点对比@Style,@Extend, AttributeModifier, @Builder和 struct 这五种方法的区别和使用场景。


Styles装饰器


    学过Android开发的小伙伴知道Android中有样式Style概念,我们定义好一个样式,那么就可以在各个组件中使用。从而保持每个组件有一样的属性。


    同理,鸿蒙中也可以使用样式。比如我们UI上的按钮具有相同的宽,高,背景色和边角距。那么我们就可以定义一个Style,每次定义按钮时候,只需要将Style赋给按钮就可以实现该属性,从而避免重复代码的书写。



  • 代码说明

image.png


    如图,在当前页面内定义一个方法,使用装饰器Styles修饰,修饰后,方法内部就可以直接通过 .属性 的方式来定义属性了。方法定义完后,下方button里可以直接使用该方法。虽然我这里方法命名为commonButton,但是实际上所有基础控件都可以使用该方法使用里边的属性,比如下方的Text组件。



  • Style特点


  1. 对于定义的方法,无法使用export修饰。
    这也就意味着,我们抽离的通用属性,只能在当前页面内的组件上使用,换个页面就不可以了,无法做到全局所有页面通用。
  2. 对于定义的方法,只能定义组件的通用属性。
    比如宽高,背景色等。对于一些控件特有属性是无法定义的。比如Select组件的selectedOptionFont特有属性无法定义。
  3. 方法不支持传递入参。
    意味着该样式无法做到动态修改,只要定义好就无法修改。比如定义好宽高为30,而某个组件宽要求为40,其他属性都不变,那这个方法也没法用了。
  4. 方法为组件通用属性,故所有组件都可以引用方法。

Extend装饰器


    对于Styles装饰器的第2点限制,鸿蒙推出了解决方案,那就是使用@Extend装饰器。


    Extend装饰器需要我们在使用时候指定定义哪个组件的属性,是专门抽离指定组件的。



  • 代码说明

image (1).png


    Extend要求必须定义方法为在当前文件的全局定义,且也不能够export,同时定义时候需要指定是针对哪个控件。如图指定了控件Select,然后就可以指定Select的专有属性了。



  • Extend特点


  1. 方法不支持export。
    和Styles一样,无法真正做到为所有页面抽离出都可用的属性方法。
  2. 方法只能定义为当前页面内的全局方法。
    一定程度上全局方法存在引用GlobalThis,具体副作用未知。
  3. 方法需要指定控件,其他控件无法使用,只能对专有控件做到了抽离
  4. 方法可以传入参。
    相比Styles, 可以在其他属性不变的情况下,只修改其中的部分属性。

AttributeModifier


    对于上述两个装饰器都存在一个相同的限制,就是无法做到全局所有文件都可以公用。


    AttributeModifier的出现可以变相的解决这个问题。AttributeModifier本意是用于自定义控件中设置属性用的。但是我们在这里也可以通过这个机制,来实现全局所有文件中控件均可通用的属性。



  • 代码说明

image (2).png


    该Modifier只能针对专用控件,比如我要抽离一个通用的TextInput,那么我可以如上图所定义。


    需要实现一个接口 AttributeModifier,接口泛型定义和我们想要给哪个控件使用有关,比如我们想给TextInput使用,那么泛型就是 TextInputAttribute,如果给Column使用,那么泛型就是ColumnAttribute,以此类推。


    在该接口的实现方法中,定义控件的属性。



  • 布局中使用

image (3).png



  • 自定义属性

    我们还可以自定义部分属性,只需要修改TextInputAttribute,例如我们想自定义字体大小。可以定义变量。


image (4).png



  • 使用

image (5).png



  • AttributeModifier特点


  1. 可以全局给所有页面中的控件使用
  2. 可以自定义任何控件中的属性,包括特有属性
  3. 可以通过修改代码做成链式调用
  4. 该方法需要new对象,比较笨重,需要书写较多代码

@Builder


    上述说的都是针对单独的控件,如果我们想抽离一个通用的布局呢?或者我们的控件就是固定的可以拿来到处使用。


    比如我有一个Text,各种属性固定,只是文案不同,那么我使用上述几种都比较麻烦,需要书写较多代码。那么这个时候就可以使用builder了。



  • 代码

image (6).png


    我们可以在任意需要展示该Text的地方使用,直接调用该方法,对应位置就可以显示出内容了。原理相当于是将方法内的控件代码放到了对应的位置上。



  • 使用

image (7).png



  • @Builder特点


  1. 定义好方法后,需要拿Builder装饰器修饰,可以在任何一个页面内调用方法使用。
  2. 可以通过方法传递入参
  3. 无法通过方法拿到控件对象,只能在方法里操作控件属性
  4. 除了单一控件,还可以定义布局,布局中存在多个控件的情况
  5. 轻量级
  6. 方法即拿即用,代码量少

struct


    有时候,我们可能页面中存在大量如下UI:


image (8).png


    对于这种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 来保存用户输入的数字,然后在用户点击按钮时候传给点击事件方法,交给调用者调用。



  • 使用

image (9).png


    点击设置按钮,点击事件触发,a直接赋值。



  • struct特点


  1. 可以封装复杂组件,自定义组件
  2. 可以维护变量存储用户输入输出
  3. 可以所有页面全局使用
  4. 可以自定义属性
  5. 无法链式设置属性

对比各个使用场景


    实际编程中,一般都是混合相互配合使用,没必要单独硬使用哪一个。



  1. style
    可以用来定义一些通用属性,比如背景色,边角据等
  2. Extend
    对于页面中一些特殊的控件,用的地方较多时候,可以抽离方法
  3. AttributeModifier
    如果Extend无法满足,那么选择这个
  4. Builder
    对于布局控件的属性变化不大,但是用的地方多时候使用,比如定义一个分割线。
  5. struct
    涉及到用户输入输出时候,相关控件可以抽离封装,避免页面内上方定义太多变量,不好维护。

作者:MinQ
来源:juejin.cn/post/7374293974577692706

0 个评论

要回复文章请先登录注册