当前位置: 首页 > article >正文

从零开始开发纯血鸿蒙应用之UI封装

从零开始开发纯血鸿蒙应用

  • 一、题引
  • 二、UI 组成
  • 三、UI 封装原则
  • 四、实现 lib_comps
    • 1、封装 UI 样式
      • 1.1、attributeModifier 属性
      • 1.2、自定义`AttributeModifier<T>`类
    • 2、封装 UI 组件
  • 五、总结

一、题引

在开始正文前,为了大家能够从本篇博文中,汲取到代码外的东西,即编程思想,我想问问每一个屏幕前的读者,一个问题,那就是:所谓UI,在你看来,可以划分成哪些组成?

二、UI 组成

UI,即 User Interface,译为用户界面,是一个应用提供给用户直接进行查看和操作的内容。
虽然,还不知道大家脑海中对于UI组成,都有什么样的看法?但我这里需要说的是,我认为一个合格的UI,当是由结构样式响应有机组合成的,而合格的UI封装也应该就此进行。

UI 实际上就像大家日常生活中居住的房子,一个住起来感到惬意的房间,必然先后经历房壳子和装潢后的房间,这两个截然不同的阶段;在装潢阶段,房间的墙壁或被贴上墙纸、或被刷上墙漆,墙壁上的某些位置上出现的各种插座、开关面板,则是早在搭建房壳子的时候一并完成的,然而它们实际会控制什么样的电器,则是在装潢阶段进行的。

三、UI 封装原则

再问大家一个问题:进行UI公共组件的封装时,是习惯于将结构、样式和响应,杂糅在一起进行封装,还是采用粒度更细化的方式进行呢?

我进行UI封装时,会有一个很明确的行为准则:单一职能原则,一个封装实现体(class或struct),应该只承担粒度从小到大顺序中的某一个职能,比如只负责确定结构、或只负责确定样式。
当然了,前提是所使用的UI开发框架支持相应的粒度抽取。

就鸿蒙应用SDK来说,最新版本的API里,样式已经允许与结构、响应独立,封装在不同的ets文件中,具体如何实现,后文细说;而对于结构和响应,就像造房子一样,墙壁上开关面板的走线管道,只能在墙壁完整造出来前布置好,UI结构封装时必须预留响应的载入通道,通常就是允许传入一个函数

四、实现 lib_comps

现在,开始对工程里面的 lib_comps 模块进行实现,先看一下该模块下的目录结构:
lib_comps模块目录结构
在 ets 源码目录下,我细分出 componentsstyle两个子目录,前者负责UI结构和响应载入通道的封装,后者负责UI样式的封装。

1、封装 UI 样式

进行网页实现的时候,大家经常会用到类似如下的代码:

<div class="container">
	<p class="normal-text">
		文本
	</p>
</div>

html 标签的 class 属性,用于将提前封装在CSS文件中的各种样式进行载入的位置,只有这样,才可能进行样式的独立封装。现在,我可以明确地告诉大家,鸿蒙API 12 之后也能如此进行了:
在这里插入图片描述

1.1、attributeModifier 属性

在我看来,该属性是相当重要而有用的,它让鸿蒙 UI 实现的灵活度得到了进一步的增强。
在这里插入图片描述
虽然,官方将其解释为“动态设置组件的属性方法”,但我却更愿意将其看待成鸿蒙组件的class属性,因为,在鸿蒙UI实现中,组件样式也是通过属性方法进行设置的,比如下面:

Row(){

      Text(this.fileName.split(".")[0])
        .fontSize(25)
        .fontWeight(FontWeight.Regular)

      Text(this.fileName.split(".")[1])
        .fontSize(20)
        .fontWeight(FontWeight.Lighter)

      Text('操作')
        .fontSize(25)
        .fontWeight(FontWeight.Regular)
        .bindMenu(this.OptionMenu())

    }.width("100%")
    .height("10%")
    .alignItems(VerticalAlign.Center)
    .justifyContent(FlexAlign.SpaceBetween)

那么,传入 attributeModifier 属性里面的自定义AttributeModifier<T>类里面,只设置样式相关的属性,那么,实际上对应的自定义AttributeModifier<T>类就相当是一个css了。

1.2、自定义AttributeModifier<T>

实现时,要与目标组件的类型相一致,也就是说,如果是要为 column 组件的 attributeModifier 属性赋值,那么泛类型 T 对应的具体类型就是 ColumnAttribute。

通常,应用里面使用的 column 样式不会只有一种,所以,不妨把所有适用于 column 的样式封装,都放在同一份 ets 文件中,从而就有了前面 lib_comps 工程目录结构图里面的 ColumnStyles.ets 文件。

下面,以 RootTopColumn 类为例,讲解如何进行自定义AttributeModifier<T>类的封装:

export class RootTopColumn implements AttributeModifier<ColumnAttribute> {
  private columnColor: ResourceColor;

  constructor(columnColor: ResourceColor) {
    this.columnColor = columnColor;
  }

  applyNormalAttribute(instance: ColumnAttribute): void {
    instance.width("100%")
      .height("100%")
      .backgroundColor(this.columnColor)
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Start)
  }
  
  applyPressedAttribute(instance: ColumnAttribute): void {
  }
  
  applyFocusedAttribute(instance: ColumnAttribute): void {
  }
  
  applyDisabledAttribute(instance: ColumnAttribute): void {
  }
  
  applySelectedAttribute(instance: ColumnAttribute): void {
  }
  
}

首先,自定义AttributeModifier<T>类和普通类一样,可以定义字段并通过构造函数进行初始化或赋值;其次,可以一次性封装5种不同的样式,用以针对组件的普通状态、按下状态、获焦状态、不可用状态和选择状态,不同的状态样式由各自对应的 applyXXXXAttribute 方法实现,这些方法统一接受一个 T 类型参数,对于 Column 组件来说,就是 ColumnAttribute;最后,在具体渲染时调用哪一个 applyXXXXAttribute 方法,由 UI 渲染引擎自行判断,开发者无需关心。

实现每个具体的 applyXXXXAttribute 方法时,可以从样式的如下几方面进行:

  • 尺寸:width 属性和 height 属性
  • 颜色:背景色 backgroundColor 属性,前景色foregroundColor属性
  • 对齐方式:alignItem 属性和 justifyContent 属性
  • 边距:外边距 margin 属性和内边距 padding 属性
  • 边框:boder 属性

当然了,鸿蒙 UI 组件支持的样式属性绝不止这些,只不过上面这些是比较常用的,基本足够应付大多数UI实现所需的样式了。

当对应的 自定义AttributeModifier<T>类,需要用在最外层的容器上,那么尺寸就应该是与屏幕尺寸相同,这里的 RootTopColumn 就是这样的,所以,我才会在 applyNormalAttribute 函数中,将尺寸都设置为百分百;同时,因为预期布局方式是水平居中、垂直局始,所以,只剩下背景色需要通过类构造函数进行传入。

2、封装 UI 组件

由于,鸿蒙API中,UI 结构和响应,没办法彻底解耦,所以,结构和响应就没有按照彻底独立的方式进行封装,而是直接上升到组件维度进行封装。

在组件中,按钮和对话框应该算得上是使用频率很高的组件了,所以,有必要为此封装相应的公共组件,以黑色背景的按钮来说,可以用类似如下的代码进行封装:

@Component
export struct BlackBtn {
  private buttonWidth: Length = "75%"
  private buttonHeight: Length = 55
  @Prop @Require text: ResourceStr = ""
  private textSize: string | number | Resource = 20
  private action?: () => void = () => {
  }
  private btnStyle: BlackButton = new BlackButton(this.buttonWidth, this.buttonHeight)

  build() {
    Button() {
      Text(this.text)
        .WhiteText(this.textSize)
    }.attributeModifier(this.btnStyle)
    .onClick(this.action)
  }
}

在鸿蒙UI中,所有的组件都是用注解了 Component 的 struct 体承担实现,并且该 struct 体中必须实现 build 方法,具体的 UI 内容实现放在 build 方法里,build 方法里不允许使用非UI表达式,除了少数允许的条件表达式外的一切逻辑表达式,都不允许直接放在 build 方法体里面。

由于注解了 Component 的 struct 体,跟类一样,允许从外部传入参数,所以,具体的内容和样式参数,都可以从外部传入;当然了,也可以为它们都设置初始化值;如果某个属性值必须要外部传入,可以在相应的字段前用 Require 注解。

对话框的封装,也是差不多的形式:
在这里插入图片描述
从上面的代码可以看出,封装UI组件的时候,可以根据内容的多寡,决定是否继续划分出更小的组件。

五、总结

受限于篇幅,我这里并没有将 lib_comps 模块的全部实现代码贴出来,大家不妨前往lib_comps仓库,查看具体的实现代码。

总而言之,在封装UI的时候,应当在遵循单一职能的基础上,按粒度从小到大的形式进行内容拆分和实现,一块砖一块砖地码,万里长城才能码得稳、码得好!


http://www.kler.cn/a/457467.html

相关文章:

  • PCA降维算法详细推导
  • 详细的一条SQL语句的执行流程
  • IDEA试用总结
  • SpringCloudAlibaba实战入门之路由网关Gateway过滤器(十三)
  • 每日一学——监控工具(Grafana)
  • 微信小程序滑动解锁、滑动验证
  • HarmonyOS NEXT应用开发实战:一分钟写一个网络接口,JsonFormat插件推荐
  • java开发配置文件集合
  • E6 中的 扩展运算符(Spread) 和 剩余运算符(Rest)
  • 游戏陪玩系统:国际版JAVA游戏陪玩系统源码陪练APP源码H5源码电竞系统源码支持Android+IOS+H5
  • 最新常见的图数据库对比,选型,架构,性能对比
  • 大数据技术-Hadoop(二)HDFS的介绍与使用
  • 深度学习笔记(6)——循环神经网络RNN
  • 折腾日记:如何让吃灰笔记本发挥余热——搭建一个相册服务
  • 工程师如何平衡工作和生活?
  • redis开发与运维-redis04-redis客户端Jedis与连接池及客户端异常模拟
  • python学习笔记——类和对象
  • 道路倒角 三角网 两侧偏移
  • 2025最新植物大战僵尸杂交版3.0.2版本
  • 埃斯顿机器人程序案例多个点位使用变量
  • 微服务架构与 Spring Cloud 相关技术解读
  • 程序猿成长之路之设计模式篇——结构型设计模式
  • C 中如何在For中生成不重复的随机数
  • 【Chrome】浏览器提示警告Chrome is moving towards a new experience
  • 邮件传输协议
  • Linux系统编程(一)--基础指令