HarmonyOS鸿蒙应用开发——ArkUI组件封装最佳实践

news2024/10/7 16:18:48

文章目录

    • 背景与案例描述
    • 静态注册属性-封装UI组件
    • 动态注册属性-封装UI组件
    • 总结

背景与案例描述

在应用开发中,对一些频繁使用的业务UI组件常常会进行一层封装,提取到公共基础库中实现组件的复用,避免类似的逻辑重复编写,减少代码冗余,从而提高开发效率,同时也降低了业务模块间的耦合,可维护性与扩展性会更强,其他开发者在需要时,只需简单地调用或实现这些组件提供的接口,即可快速完成所需功能的开发,很大程度上可以提高团队的效率和代码质量。

在ArkUI中定义一个组件是很简单的,通过@Component装饰器、struct关键字修饰即可,如下:

@Component
export struct Toolbar {
    build(){
    
    }
}

下面写一个Toolbar组件为例,如何循序渐进封装一个UI组件,做到灵活定制,可复用性强的组件,效果图:

在这里插入图片描述

Toolbar组件中有四个元素,分别是返回、标题、分享、更多,其中标题是Text组件,其他元素是Image组件实现,是可点击的。

静态注册属性-封装UI组件

首先定义对外的成员属性,用@Prop装饰器声明用于接收父组件的参数。

@Component
export struct Toolbar {
  // 注释1                
  @Prop title: string | ResourceStr
  @Prop titleColor: ResourceStr | Color
  @Prop titleSize: ResourceStr | number
  // 注释2
  onBack: Function = () => {
  }
  onMore?: () => void
  onShare?:() => void
 } 
  • 注释1:对外的公开属性,接收来自父组件的参数
  • 注释2:点击事件的回调方法,让定制业务逻辑交给使用者去实现。

然后在build()方法中实现UI布局,如下:

  build() {
    Row() {
      Image($r('app.media.ic_toolbar_back'))
        // 注释1
        .imageStyle()
        .onClick(() => {
          this.onBack()
        })
      
      Text(this.title)
        .textAlign(TextAlign.Start)
        .layoutWeight(1)
        .fontColor(this.titleColor)
        .padding({ left: '10vp', right: '10vp' })
        .fontSize(this.titleSize)

      Image($r('app.media.ic_toolbar_share'))
        // 注释1
        .imageStyle()
        .onClick(()=>{
          if (this.onShare) this.onShare()
        })

      Image($r('app.media.ic_toolbar_more'))
        // 注释1
        .imageStyle()
        .margin({ left: '12vp' })
        .onClick(()=>{
          if (this.onMore) this.onMore()
        })

    }
    .backgroundColor(Color.Pink)
    .padding({ left: '10vp', right: '15vp' })
    .height('50vp')
    .width('100%')
  }

注释1处是Image组件的公共样式,通过@Extend装饰器进行了抽离成函数,其函数必须定义在文件顶层作用域中,不能在类中定义。

@Extend(Image)
function imageStyle(){
  .size({ height: '100%', width: '25vp' })
  .objectFit(ImageFit.Auto)
}

上面就完成了Toolbar组件常规封装了,导入使用传入参数。

 Toolbar({
        title: '标题',
        titleSize: '25vp',
        titleColor: Color.Black,
        onBack: () => {
          router.back()
        }
      })

你会发现上面的封装有什么缺陷吗,如果封装组件需要支持样式的动态化时,此时就要在封装组件中新增对应的成员属性,比如在Toolbar组件中,需要修改标题对齐方式、字体粗细等等 ,就需要定义这些特定的属性。如果封装组件是一个组合式组件(由多个组件组合实现),每个类型组件都有自己特有属性,这样整合起来是不是要新增很多成员属性来接收外部的参数呢。

以Text组件的属性为例,如果定义这么多成员属性来接收外部的参数,是一个地狱性的设计。

在这里插入图片描述

这种静态注册属性的方式显然是不太合理的,在处理属性动态化有点力不从心,其扩展性和可维护性差,如果你是封装一个对外开源UI组件,开发者的需求是多样性的,是很难满足个性化需求的。

对此有没更佳的解决方案呢,当然有,系统为每个组件提供一个attributeModifier属性方法,正好可以解决此类的问题。

动态注册属性-封装UI组件

通过AttributeModifier来动态注册属性的方式来封装UI组件,解决静态注册属性的问题,改方式可以将属性从组件分离解耦出来,由外部使用者按需设置。

先初步了解下AttributeModifier接口的属性方法。

在这里插入图片描述

  • applyNormalAttribute:定义组件正常状态的属性值
  • applyPressedAttribute:定义组件按下的属性,比如点击事件
  • applyFocusedAttribute:定义焦点相关的属性
  • applyDisabledAttribute:定义组件禁用属性
  • applySelectedAttribute:定义选择属性

接下来通过AttributeModifier来改造Toolbar封装组。

1、在封装组件中定义对应的Modifier属性。

export struct Toolbar2 {
  @Prop title: StringOrNull
  // 注释1
  @Prop titleModifier: AttributeModifierOrNull<TextAttribute>
  @Prop backModifier: AttributeModifierOrNull<ImageAttribute>
  @Prop shareModifier: AttributeModifierOrNull<ImageAttribute>
  @Prop moreModifier: AttributeModifierOrNull<ImageAttribute>

注释1处从上到下分别是标题、返回、分享、更多按钮的AttributeModifier成员属性,接收外部传入的AttributeModifier类实例。其中AttributeModifierOrNull是自定义的别名联合类型。

export type  AttributeModifierOrNull<T> = AttributeModifier<T> | null

2、接着自定义实现类来实现AttributeModifier接口,在实现类可以定义set方法来实现链式调用方式设置属性。这样我们定义一个BaseModifier基础类来实现AttributeModifier接口,主要是为了后期扩展。

class BaseModifier <T> implements AttributeModifier<T> {
  applyNormalAttribute(instance: T): void {
  }
}

然后在封装组件Toolbar中的aboutToAppear()方法中初始化默认值。

如果不初始化默认值,如果外部没有传入对应的Modifier实例就会抛出异常闪退。

  aboutToAppear(): void {
    if (!this.titleModifier) {
      this.titleModifier = new BaseModifier<TextAttribute>()
    }
    if (!this.backModifier) {
      this.backModifier = new BaseModifier<ImageAttribute>()
    }
    if (!this.shareModifier) {
      this.shareModifier = new BaseModifier<ImageAttribute>()
    }
    if (!this.moreModifier) {
      this.moreModifier = new BaseModifier<ImageAttribute>()
    }

  }

最后将成员变量Modifier属性关联到对应的组件中,如下:

 Image($r('app.media.ic_toolbar_back'))
        .imageStyle()
        // 注释1
        .attributeModifier(this.backModifier)

      Text(this.title)
        .textAlign(TextAlign.Start)
        .layoutWeight(1)
        .padding({ left: '10vp', right: '10vp' })
        // 注释1
        .attributeModifier(this.titleModifier)

      Image($r('app.media.ic_toolbar_share'))
        .attributeModifier(this.shareModifier)
        .imageStyle()

      Image($r('app.media.ic_toolbar_more'))
        .imageStyle()
        .margin({ left: '12vp' })
        // 注释1
        .attributeModifier(this.moreModifier)

注释1处便是Modifier属性的设置,建议将设置关联attributeModifie()方法放在组件的最后调用链处,这样外部设置可以覆盖掉组件内的属性值。

3、外部使用者自定义类实现AttributeModifier接口,或者继承BaseModifier基础类。

class TitleModifier implements AttributeModifier<TextAttribute> {
  private _fontWeight?: FontWeight
  private _textAlign?: TextAlign

  public setFontWeight(value: FontWeight): TitleModifier {
    this._fontWeight = value
    return this
  }

  public setTextAlign(value: TextAlign): TitleModifier {
    this._textAlign = value
    return this
  }

  applyNormalAttribute(instance: TextAttribute): void {
    // 设置属性
    instance.fontColor(Color.Blue)
    instance.fontSize('25vp')
    instance.textAlign(TextAlign.Center)
    instance.fontWeight(FontWeight.Bold)
  }
}

class BackModifier extends  BaseModifier<ImageAttribute> {
  private static instance: BackModifier
  public static getInstance(): BackModifier {
    if (BackModifier.instance) {
      return BackModifier.instance
    } else {
      return new BackModifier()
    }
  }
  
  applyPressedAttribute(instance: ImageAttribute): void {
  // 点击返回
    router.back()
  }
}

最后是外部使用TitleModifier和BackModifier,效果图:

在这里插入图片描述

 Toolbar2({
        title: '标题',
        backModifier: BackModifier.getInstance(),
        titleModifier: new TitleModifier()
      })

其中BackModifier是一个单例模式,如果多处调用可以避免创建多个实例带来的性能损耗,在组件数量少可能很难体现这价值,如果是在长列表场景中可能会体现出来。

总结

静态注册属性封装UI组件的特点:使用简单,由于要手动定义属性,导致在可维护性和可扩展性上有很大的局限性,对于简单的UI组件,静态注册可能是可行的封装方式,易用易理解,但是比较复杂的组合式ui组件,尤其是那些对动态设置要求较高的场景时,静态注册就显得不那么适用了,需要定义众多的成员属性,无法根据实际需求灵活地按需注册属性。

动态注册属性封装UI组件的特点:是通过组件的AttributeModifier来实现的,相较于静态注册方式,尽管操作更为复杂,但可以弥补静态注册的缺陷。这种方法可以按需设置属性,从而体现出极高的灵活性和扩展性,充分体现了封装的精髓,同时也遵循了单一职责设计原则,功能清晰划分明了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1806037.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端技术】 ES6 介绍及常用语法说明

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

LabVIEW缝缺陷图像标注库

LabVIEW缝缺陷图像标注库 开发了一个基于LabVIEW平台构建的船舶焊缝缺陷图像标注库。该库旨在通过高效和简洁的方式处理和标注船舶焊缝缺陷图像&#xff0c;提高缺陷识别的准确性和效率&#xff0c;进而保障船舶的结构安全。 项目背景 在船舶制造过程中&#xff0c;焊接质量…

递归【1】(全排列andN皇后)(排列型回溯)

全排列 分治与递归 递归是实现分治的一种方法 思想思路 题目&#xff1a; 全排列i 我这样直接输出会多输出一个空行&#xff08;最后一个\n&#xff09; #include<stdio.h>using namespace std; const int maxn10; int an[maxn]; int n; bool hash[maxn]{0}; int c0…

STM32-15-DMA

STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU STM32-14-FSMC_LCD 文章目录 STM…

【Java面试】十七、并发篇(上)

文章目录 1、synchronized关键字的底层原理&#xff1a;Monitor2、synchronized相关2.1 为什么说synchronized是重量级锁2.2 synchronized锁升级之偏向锁2.3 synchronized锁升级之轻量级锁 3、Java内存模型JMM4、CAS4.1 CAS流程4.2 CAS底层实现 5、volatile关键字的理解5.1 可见…

区块链(Blockchain)调查研究(一)

文章目录 1. 区块链是什么&#xff1f;2. 区块链分类和特点3. 区块链核心关键技术3.1 共识机制3.2 密码学技术3.4 分布式存储3.5 智能合约 4. 区块链未来发展趋势5. 区块链能做什么、不能做什么&#xff1f;5.1 第一部分5.2 第二部分5.3 第三部分&#xff08;结论&#xff09; …

八爪鱼现金流-019-个人对接支付,个人网站支付解决方案

背景&#xff1a; 随着用户量不断增加&#xff0c;服务器成本越来越大。想着实现会员制回点服务器成本。 业务场景分析&#xff1a; 用户在站点上付款 -----> 我监听到付款金额 -----> 给用户开通会员 调研&#xff1a; 支付宝和微信官方支付接口&#xff1a;基本都需…

MOS管十大品牌

MOS管十大品牌-场效应管品牌排行-MOS管品牌-Maigoo品牌榜

统计学研硕大数据统计练手11

统计学论文练手作业 题目AI绘图仅供欣赏 题目 2024年的《政府工作报告》中提出“深化大数据、人工智能等研发应用,开展“人工智能+”行动,打造具有国际竞争力的数字产业集群”,请同学们结合自己工作的所在行业或领域谈一谈大数据技术在人工智能时代下的应用现状、存在的问…

VSC++: 民意调查比例法

void 民意调查比例法() {//缘由https://bbs.csdn.net/topics/396521294?page1#post-411408461从题目描述看&#xff1a;902/3~300.7&#xff0c;1498/5~299.6也就是大约求2个数的公约数&#xff0c;并使得这个公约数尽量求出最小误差&#xff1f;且商小于某值。int a 0, aa …

JS(JavaScript)的引用方式介绍与代码演示

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Mamba v2诞生:3 SMA与Mamba-2

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

从品牌的角度看老字号五芳斋如何“粽”出年轻味?

端午划着龙舟的浆又来到了我们身边&#xff0c;咸鸭蛋和粽子已经裹上精美的包装在货架上等待着它们的“有缘人”&#xff0c;其实长期以来&#xff0c;说起吃粽子除了“甜咸口”的辩论赛&#xff0c;貌似在产品上却并没有太多的创新&#xff0c;但近几年随着消费市场的不断创新…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 6月9日,星期日

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年6月9日 星期日 农历五月初四 1、 人社部&#xff1a;个人养老金开户人数已超6000万&#xff0c;其中31岁至40岁的中高收入人群是开户、缴费和购买产品的主力军。 2、 医保局刊文&#xff1a;研究显示集采仿制药替代原研药…

网关API(SpringCloudGateway)如何自定义Filter

1.前言 SpringCloud 虽然给我们提供了很多过滤器&#xff0c;但是这些过滤器功能都是固定的&#xff0c;无法满足用户的各式各样的需求。因此SpringCloud提供了过滤器的扩展功能自定过滤器。 开发者可以根据自己的业务需求自定义过滤器。 2. 自定义 GatewayFilter(局部过滤器)…

LangChain4j实战

基础 LangChain4j模型适配: Provider Native Image Sync Completion Streaming Completion Embedding Image Generation Scoring Function Calling OpenAI ✅ ✅ ✅ ✅ ✅ ✅ Azure OpenAI ✅ ✅ ✅ ✅ ✅ Hugging Face ✅ ✅ Amazon Bedrock ✅ ✅…

STM32中ADC在cubemx基础配置界面介绍

ADCx的引脚,对应的不同I/O口&#xff0c;可以复用。 Temperature :温度传感器通道。 Vrefint :内部参照电压。 Conversion Trigger: 转换触发器。 IN0 至 IN15,是1ADC1的16个外部通道。本示例中输出连接的是ADC2的IN5通道&#xff0c;所以只勾选IN5.Temperature Sensor Cha…

【C++】:模板初阶和STL简介

目录 一&#xff0c;泛型编程二&#xff0c;函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则 三&#xff0c;类模板3.1 类模板的定义格式3.2 类模板的实例化 四&#xff0c;STL简介&#xff08;了解&#xff09;4.1 什…

将字符串str1复制为字符串str2

定义两个字符数组str1和str2&#xff0c;再设两个指针变量p1和p2&#xff0c;分别指向两个字符数组中的有关字符&#xff0c;通过改变指针变量的值使它们指向字符串中的不同的字符&#xff0c;以实现字符的复制。编写程序&#xff1a; 运行程序&#xff1a; 程序分析&#xff1…

STM32H750启动和内存优化(分散加载修改)

前些日子有个朋友一直给我推荐STM32H750这款芯片&#xff0c;说它的性价比&#xff0c;说它多么多么好。于是乎&#xff0c;这两天试了试&#xff0c;嚯&#xff0c;真香&#xff01;我们先看看基本配置 这里简单总结下&#xff0c;cortex-m7内核&#xff0c;128k片内flash …