鸿蒙系列--装饰器

一、基础UI组件结构

        每个UI组件需要定义为@Component struct对象,其内部必须包含一个且只能包含一个build(){}函数,用于绘制UI;struct之内、build()函数之外的地方用于存放数据。

二、基本UI装饰器

@Entry

装饰struct,页面的入口

@Component

装饰struct,表示该struct具有基于组件的能力

@Entry
@Component
struct TestPage {build() {……}
}

三、数据装饰器

@State 父子相互独立

  • 装饰的变量是组件的局部变量,必须本地初始化,可通过构造参数赋值
  • 当该数据被修改时,所在组件的build()方法会被重新调用,会重新绘制所在UI
子组价:
@Component
export struct ComponentPage {@State count: number = 0private toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是子组件,${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))}.width('100%')}}
}
父组件:
import { ComponentPage } from "./ComponentPage"@Entry
@Component
struct StatePage {@State count: number = 0private toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是父组件,当前值: ${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))//使用默认初始化值ComponentPage()//设置count初始值为:20ComponentPage({ count: 20 })}.width('100%')}.height('100%')}
}
描述:
  • 当被点击之后修改了count的值,页面会重新绘制UI
  • 子组件中的count和父组件的count互不影响
  • 可以给子组件构造方法设置初始值
  • 使用@State修饰的变量必须初始化
效果图:

@Prop 父子单向同步

  • 继承@State的所有功能
  • 被其装饰的变量可以和父组件建立单向同步关系。@Prop装饰的变量是可变的,但修改不会同步回父组件,当父组件的@State变化时,本地修改的@Prop会被覆盖
子组件:
@Component
export struct ComponentPage {@Prop count: numberprivate toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是子组件,${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))}.width('100%')}}
}
父组件:
import { ComponentPage } from "./ComponentPage"@Entry
@Component
struct StatePage {@State count: number = 0private toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是父组件,当前值: ${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))ComponentPage({ count: this.count })}.width('100%')}.height('100%')}
}
描述:
  • 将父组件的count设置到子组件使用的@Prop修饰的变量时,父组件与子组件这时建立起单向同步
  • 父组件修改值后,子组件跟着修改,子组件修改值父组件不受影响
  • 使用的@Prop修饰的变量不能自己初始化
效果图:

@Link 父子双向同步

  • @Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。
  • @Link装饰的变量与其父组件中的数据源共享相同的值
  • @Link装饰器不能在@Entry装饰的自定义组件中使用
子组件:
@Component
export struct ComponentPage {@Link count: numberprivate toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是子组件,${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))}.width('100%')}}
}
父组件:
import { ComponentPage } from "./ComponentPage"@Entry
@Component
struct StatePage {@State count: number = 0private toggleClick() {this.count += 1}build() {Row() {Column({ space: 20 }) {Button(`这是父组件,当前值: ${this.count}`).fontSize(24).onClick(this.toggleClick.bind(this))ComponentPage({ count: $count })}.width('100%')}.height('100%')}
}
描述:
  • 父组件通过$count来和子组件的@Link修饰的值绑定
  • 绑定之后实现父子双向绑定,修改一端,另一组件也随之变化
  • 使用@Link不能自己初始化
效果图:

@State、@Prop与@Link的异同

相同点:

  • 都会引起UI重绘
  • 内部私有

不同点:

不同点@State@Prop@Link
装饰内容基本数据类型,类,数组基本数据类型基本数据类型,类,数组
关联不与其他控件关联父@State -> 子@Prop 单向关联父@State <-> 子@Link 双向关联
初始化时机声明时创建组件时由参数传入创建组件时由参数传入

四、生产消费的装饰器

@Provide、@Consume

后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递

案例:

在父组件中将数据多级传递给子组件,子子组件

1.使用@Link修饰的变量进行传递
父组件:
import { ProviderSonPage } from "./ProviderSonPage"@Entry
@Component
struct ProviderPage {@State message: string = '父类A'build() {Row() {Column() {Text(this.message).fontSize(50).fontColor(Color.Red).onClick(() => {//点击文字  进行切换this.message = this.message === '父类A' ? '父类B' : '父类A'})//调用子组件ProviderSonPage({ sonMsg: $message })}.width('100%')}.height('100%')}
}
子组件:
import { ProviderGrandSonPage } from "./ProviderGrandSonPage"@Component
export struct ProviderSonPage {@Link sonMsg: stringbuild() {Column() {Text(this.sonMsg).fontSize(30).fontColor(Color.Green).onClick(() => {this.sonMsg = '我是子类'})//调用孙子组件:子类的子类ProviderGrandSonPage({ grandSonMsg: $sonMsg })}}
}
子子组件:
@Component
export struct ProviderGrandSonPage {@Link grandSonMsg: stringbuild() {Column() {Text(this.grandSonMsg).fontSize(20).fontColor(Color.Blue).onClick(() => {this.grandSonMsg = '我是子类的子类'})}}
}
总结:
  • 都需要通过一个多余被@Link修饰的变量进行传递,太过复杂,如果传递层级太深没更加明显
2.发布者订阅者模式

使用发布者Provide和订阅者Consume可以直接传递到子子组件

父组件:
import { ProviderSonPage } from "./ProviderSonPage"@Entry
@Component
struct ProviderPage {@Provide('Mes') message: string = '父类A'//也可以写成@Provide message: string = '父类A'build() {Row() {Column() {Text(this.message).fontSize(50).fontColor(Color.Red).onClick(() => {this.message = this.message === '父类A' ? '父类B' : '父类A'})//调用子组件时就不再需要传递参数ProviderSonPage()}.width('100%')}.height('100%')}
}
子组件:
import { ProviderGrandSonPage } from "./ProviderGrandSonPage"@Component
export struct ProviderSonPage {@Consume('Mes') sonMsg:stringbuild() {Column() {Text(this.sonMsg).fontSize(30).fontColor(Color.Green).onClick(() => {this.sonMsg = '我是子类'})//调用子组件时就不再需要传递参数ProviderGrandSonPage()}}
}
子子组件:
@Component
export struct ProviderGrandSonPage {@Consume('Mes') grandSonMsg:string//也可以写成@Consume message:stringbuild() {Column() {Text(this.grandSonMsg).fontSize(20).fontColor(Color.Blue).onClick(() => {this.grandSonMsg = '我是子类的子类'})}}
}
总结:
  • 使用发布者订阅者模式,父类使用@Provide,其他需要观察的子类使用@Consume,就可以能实现双向绑定
  • 当层级很深时不需要一层一层的往下传递,直接使用发布者订阅者进行监听就能实现相同的效果
  • @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,变量类型必须相同
  • @Provide必须设置初始值,@Consume不可设置默认初始值
  • @Provide修饰的变量和@Consume修饰的变量是一对多的关系

效果图:

五、状态变量更改通知

@Watch:使用观察者模式的装饰器,但该装饰器不是触发变量变化,而是绑定一个函数,当@Watch变量变化时,调用该函数

@Watch和自定义组件更新

子组件:
@Component
export struct TotalViewPage {@Prop @Watch('onCountUpdated') count: number;@State total: number = 0;// @Watch 回调onCountUpdated(propName: string): void {this.total += this.count;}build() {Text(`Total: ${this.total}`)}
}
父组件:
import {TotalViewPage} from "./TotalViewPage"@Entry
@Component
struct CountModifierPage {@State count: number = 0;build() {Column() {Button('add to basket').onClick(() => {this.count++})TotalViewPage({ count: this.count })}}
}
描述:
  1. CountModifier自定义组件的Button.onClick点击事件自增count
  2. 由于@State count变量更改,子组件TotalView中的@Prop被更新,其@Watch('onCountUpdated')方法被调用,更新了子组件TotalView 中的total变量
  3. 子组件TotalView中的Text重新渲染

@Watch与@Link组合使用

bean对象:PurchaseItem
export class PurchaseItem {static NextId: number = 0;public id: number;public price: number;constructor(price: number) {this.id = PurchaseItem.NextId++;this.price = price;}
}
子类:BasketViewer
import {PurchaseItem} from "./PurchaseItem"@Component
export struct BasketViewer {@Link @Watch('onBasketUpdated') shopBasket: PurchaseItem[];@State totalPurchase: number = 0;updateTotal(): number {let total = this.shopBasket.reduce((sum, i) => sum + i.price, 0);// 超过100欧元可享受折扣if (total >= 100) {total = 0.9 * total;}return total;}// @Watch 回调onBasketUpdated(propName: string): void {this.totalPurchase = this.updateTotal();}build() {Column() {ForEach(this.shopBasket,(item) => {Text(`Price: ${item.price.toFixed(2)} €`)},item => item.id.toString())Text(`Total: ${this.totalPurchase.toFixed(2)} €`)}}
}
父类:BasketModifierPage
import {BasketViewer} from "./BasketViewer"
import {PurchaseItem} from "./PurchaseItem"@Entry
@Component
struct BasketModifierPage {@State shopBasket: PurchaseItem[] = [];build() {Column() {Button('Add to basket').onClick(() => {this.shopBasket.push(new PurchaseItem(Math.round(100 * Math.random())))})BasketViewer({ shopBasket: $shopBasket })}}
}
描述:
  1. BasketModifierPage组件的Button.onClick向BasketModifier shopBasket中添加条目
  2. @Link装饰的BasketViewer shopBasket值发生变化
  3. 状态管理框架调用@Watch函数BasketViewer onBasketUpdated 更新BasketViewer TotalPurchase的值
  4. @Link shopBasket的改变,新增了数组项,ForEach组件会执行item Builder,渲染构建新的Item项;@State totalPurchase改变,对应的Text组件也重新渲染
  5. 重新渲染是异步发生的

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.cer格式证书文件和 .pfx格式证书文件有什么区别?

这里我们将讨论.cer和.pfx文件类型之间的差异。 什么是数字证书&#xff1f; 数字证书在电子通信中用作验证身份的密码机制。我们需要这些证书来建立安全的在线通信渠道&#xff0c;并确保数字数据的隐私、真实性和正确性。 数字证书包括主题&#xff08;实体详细信息&#xf…

MyBatis学习二:Mapper代理开发、配置文件完成增删改查、注解开发

前言 公司要求没办法&#xff0c;前端也要了解一下后端知识&#xff0c;这里记录一下自己的学习 学习教程&#xff1a;黑马mybatis教程全套视频教程&#xff0c;2天Mybatis框架从入门到精通 文档&#xff1a; https://mybatis.net.cn/index.html Mapper代理开发 目的 解决…

sql:定时执行存储过程(嵌套存储过程、使用游标)

BEGINDeclare FormNo nvarchar(20) --单号Declare Type nvarchar(50) --类型Declare PickedQty float -Declare OutQty float Declare 生产量 floatDeclare 已装箱数量 float Declare 已入库数量 floatDeclare 损耗数量 float Declare 退货品出库数量 intdeclare k c…

C#:如何产生一个临时文件

在我们实际编程中&#xff0c;经常有将内容写到一个临时文件的需要。 比如&#xff1a;将网络上的图片下载下来&#xff0c;获取到图片的一些信息。 代码如下&#xff0c;看结果可知&#xff1a; 临时文件都是保存在系统临时文件夹的目录下&#xff0c;临时文件的扩展名统一…

程序媛的mac修炼手册-- 终端shell的驾驭 zsh vs bash

进入终端(Terminal)为新下载的应用配置环境&#xff0c;是Mac生产力up up的关键一步&#xff0c;更是编程小白装大神的第一步。Fake it till you make it , 硅谷大神标准路径&#xff5e; shell的基本原理 为应用配置环境&#xff0c;相当于在应用和操作系统间架桥。由此&…

手机视频监控客户端APP如何实现跨安卓、苹果和windows平台,并满足不同人的使用习惯

目 录 一、手机视频监控客户端的应用和发展 二、手机视频监控客户端存在的问题 三、HTML5视频监控客户端在手机上实现的方案 &#xff08;一&#xff09;HTML5及其优点 &#xff08;二&#xff09;HTML5在手机上实现视频应用功能的优势 四、手机HTML5…

Day22 二叉树part08 235.二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

二叉树part08 235.二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点 235. 二叉搜索树的最近公共祖先 方法一&#xff1a;递归法&#xff08;利用二叉搜索树性质&#xff09; class Solution { public:TreeNode* lowestCommonAncestor(TreeN…

【C语言】编程世界的不朽基石与未来展望

C语言&#xff0c;一种经久不衰的高级编程语言&#xff0c;自1972年由Dennis Ritchie在AT&T贝尔实验室开发以来&#xff0c;已深深扎根于编程语言的发展历程中。它既是计算机科学史上的一个重要里程碑&#xff0c;也是现代软件开发的核心支柱。从操作系统到嵌入式系统的构建…

[C#]C# OpenVINO部署yolov8实例分割模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 YOLOv8 抛弃了前几代模型的 Anchor-Base。 YOLO 是一种基于图像全局信息进行预测的目标检测系统。自 2015 年 Joseph Redmon、Ali Farhadi 等人提出初代模型以来&#xff0c;领域内的研究者们…

【Adobe Acrobat DC】弹窗:正在准备文档以供阅读,请稍候

问题描述 Acrobat打开PDF就会有这个弹窗&#xff0c;要所有扫一遍才能看&#xff0c;要等特别久 解决办法1 参考这篇博客评论区里给出的方法 1. 转到“编辑”>“首选项”>“朗读”。 2. 在“屏幕阅读器选项”下面&#xff0c;选择“仅阅读当前可见页面”。 再次打开…

程序员必知!享元模式的实战应用与案例分析

享元模式是一种减少相似对象创建和销毁的设计模式&#xff0c;通过将对象状态分为不变和可变部分&#xff0c;实现内存节省和性能提升。例如&#xff0c;在线游戏中大量玩家角色可共享相同的不变属性&#xff0c;而每人特有的可变属性则单独存储&#xff0c;享元模式使用享元类…

微信小程序-页面开发

文章目录 微信小程序第二章2. 页面开发2.1 创建开发页面2.2 修改项目首页2.3 页面的结构和样式设计2.3.1 WXML结构设计2.3.1.1 什么是WXML2.3.1.2 WXML的常见标签2.3.1.3 WXML的特点 2.3.2 WXSS样式设计2.3.2.1 什么是WXSS 2.4 组件库的使用和自定义组件2.4.1 小程序中的组件分…