HarmonyOS 有19種裝飾器
必須【2】
繪制一個頁面,這兩個肯定會用到
- @Entry
- @Component
可選【17】
- @State
- @Prop
- @Link
- @ObjectLink
- @Watch
- @Styles
- @StorageProp
- @StorageLink
- @Provide
- @Consume
- @Observed
- @Builder
- @BuilderParam
- @LocalStorageProp
- @LocalStorageLink
- @Extend
- @Concurrent
如果你有一定編程基礎,應該在你所熟悉的語言領域已見過這種形式。
@format("Hello, %s")
helloWorld: string;
復制
@Deprecated
private static void helloWord(){
System.out.println("這個方法已經(jīng)不推薦使用");
}
復制
@ParamMetadata("ClassThree", 5)
class HelloWorld {
int timeYear;
}
復制
@RestController
public class HelloWorldController {
}
復制
@interface HelloWorldObject : NSObject {
}
復制
裝飾器
鴻蒙OS開發(fā) | 更多內容↓點擊 | HarmonyOS與OpenHarmony技術 |
---|---|---|
鴻蒙技術文檔 | 開發(fā)知識更新庫gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md 在這。 |
@Entry @Component
@Entry //這是一個頁面
@Component //頁面中有一個視圖容器,即根布局 Row()
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}.width('100%')
}.height('100%')
}
}
}
復制
@State
組件內狀態(tài)更新(即,變量內容發(fā)生變化,組件自動刷新)
@Entry //這是一個頁面
@Component //頁面中有一個視圖容器,即根布局 Row()
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button('更新this.message內容')
.onClick( ()= >{
this.message = 'HarmonyOS'
})
}.width('100%')
}.height('100%')
}
}
復制
@Link
父組件與子組件雙向同步數(shù)據(jù)(即,父組件和子組件都可以更新父組件已關聯(lián)的數(shù)據(jù))
NOTE : 子組件中的 @Link 變量,不能初始化。
import { TestChild } from './TestChild'
@Entry //這是一個頁面
@Component //頁面中有一個視圖容器,即根布局 Row()
struct Index {
@State message: string = '混沌'
build() {
Row() {
// 父組件
Column( {space : 20} ) {
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.message = 'Hello Word'
})
// 子組件
TestChild({m: $message})
}.width('100%')
}.height('100%')
}
}
復制
@Component
export struct TestChild{
@Link m: string
private childCount: number = 0
build(){
Button('Child 更新文字內容')
.onClick( ()= >{
this.m = 'HarmonyOS - Child' + (this.childCount++)
})
}
}
復制
@Prop
父組件與子組件單向同步數(shù)據(jù)(即,父組件可以同步數(shù)據(jù)至子組件,子組件無法同步數(shù)據(jù)到父組件)
NOTE: 父組件的更新指的是其內容相比之前狀態(tài)發(fā)生了變化,如下代碼中,如果將count 字段內容不賦值給 message, 則子組件僅僅會更新一次內容
import { TestChild } from './TestChild'
@Entry //這是一個頁面
@Component //頁面中有一個視圖容器,即根布局 Row()
struct Index {
@State message: string = '混沌'
count: number = 0
build() {
Row() {
Column( {space : 20} ) {
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.message = 'Hello Word ' + this.count++
})
TestChild({m: this.message})
}
.width('100%')
}
.height('100%')
}
}
復制
@Component
export struct TestChild{
@Prop m: string
private childCount: number = 0
build(){
Column( {space:20} ){
Text(this.m).fontSize(30)
Button('TestChild 更新文字內容')
.onClick( ()= >{
this.m = 'HarmonyOS - Child' + (this.childCount++)
})
}.backgroundColor(Color.Pink)
}
}
復制
@Provide @Consume
父組件與子組件的子組件(官方叫法:后代組件)雙向向同步數(shù)據(jù)(即,父組件與后代組件可以相互操作 @Provide 修飾的數(shù)據(jù))
NOTE:@Provide 與 @Consume聲明的變量名必須一致
import {TestChild } from './TestChild'
@Entry //這是一個頁面
@Component // 頁面中有一個視圖容器,即根布局 Row()
struct Index {
@Provide msg: string = '混沌'
count: number = 0
build(){
Row(){
Column( {space : 20} ) {
Text(this.msg)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.msg = 'Hello World ' + (this.count++)
})
TestChild()
}.width('100%')
}.height('100%')
}
}
復制
TestChild 嵌套 TestChild2, TestChild2嵌套TestChild3
@Component
export struct TestChild{
build(){
TestChild2(){
.width('100%')
.backgroundColor(Color.Red)
.align(Alignment.Center)
}
}
}
@Component
export struct TestChild2{
build(){
TestChild3()
}
}
@Component
export struct TestChild3{
@Consume msg: string
count: number = 0
build(){
Column(){
Text(this.msg).fontSize(30)
Button('TestChild2 更新文字內容')
.onClick( ()= >{
this.msg = 'HarmonyOS - Child' + (this.count++)
})
}.backgroundColor(Color.Pink)
}
}
復制
@Observed @ObjectLink
父組件與嵌套對象或數(shù)組進行雙向向同步數(shù)據(jù)
說明
實際業(yè)務研發(fā)中,我們封裝好多類(與 @Component 修飾的組件無關),這個時候,如果要讓父組件 和 嵌套對象進行數(shù)據(jù)同步,前邊所介紹的所有裝飾器是無法做到的。
NOTE
- 子組件中@ObjectLink裝飾器裝飾的狀態(tài)變量用于接收@Observed裝飾的類的實例,和父組件中對應的狀態(tài)變量建立雙向數(shù)據(jù)綁定
- 單獨使用@Observed是沒有任何作用的,需要搭配@ObjectLink或者@Prop使用初始狀態(tài)
NOTE:
這次你會發(fā)現(xiàn) 點擊“Parent 更新文字內容”,父組件文字沒有發(fā)生變化,原因是因為有3級嵌套類如何破解?
“子組件中@ObjectLink裝飾器裝飾的狀態(tài)變量用于接收@Observed裝飾的類的實例,和父組件中對應的狀態(tài)變量建立雙向數(shù)據(jù)綁定”
// 引起此問題初始化代碼
@State b: ClassB = new ClassB(new ClassA(0));
// 修改
@State a: ClassA = new ClassA(0)
@State b: ClassB = new ClassB(a)
復制
import {ClassA, ClassB, TestChild } from './TestChild'
@Entry //這是一個頁面
@Component //頁面中有一個視圖容器,即根布局 Row()
struct Index {
@State b: ClassB = new ClassB(new ClassA(0));
build() {
Row() {
Column( {space : 20} ) {
Text(this.b.a.c + '')
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.b.a.c += 1;
})
TestChild({a: this.b.a})
}.width('100%')
}.height('100%')
}
}
復制
@Component
export struct TestChild {
@ObjectLink a: ClassA;
build(){
Column(){
Text(this.a.c + '').fontSize(30)
Button('TestChild2 更新文字內容')
.onClick( ()= >{
this.a.c += 1;
} )
}.backgroundColor(Color.Pink)
}
}
@Observed
export class ClassA {
public c: number;
constructor(c: number) {
this.c = c;
}
}
export class ClassB {
public a: ClassA;
constructor(a: ClassA) {
this.a = a;
}
}
復制
@Watch
關注某個變量狀態(tài)發(fā)生變化
NOTE:監(jiān)聽的這個變量不要放在回調方法中,讓其發(fā)生二次變化,容易導致死循環(huán)
import {ClassA, ClassB, TestChild } from './TestChild'
@Entry //這是一個頁面
@Component //頁面中有一個視圖容器,即根布局 Row()
struct Index {
@State msg: string = '混沌'
@State index: number = 0;
build(){
Row(){
Column( {space : 20} ) {
Text(this.msg + ' ' + this.index)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.index++
})
TestChild({count: this.index})
}.width('100%')
}.height('100%')
}
}
復制
NOTE:使用 @Prop 修飾的原因:感知父組件改變 count 值
@Component
export struct TestChild{
@Prop @Watch('onCountUpdated') count: number;
@State total: number = 0;
// @Watch 回調
onCountUpdated(propName: string): void {
this.total += 1;
}
build(){
Column(){
Text('HarmonyOS - Child' + this.total).fontSize(30)
Button('TestChild2 更新文字內容')
.onClick( ()= >{
this.count++
})
}.backgroundColor(Color.Pink)
}
}
復制
@LocalStorageLink @LocalStorageProp
LocalStorage是頁面級的UI狀態(tài)存儲,通過@Entry裝飾器接收的參數(shù)可以在頁面內共享同一個LocalStorage實例。LocalStorage也可以在UIAbility內,頁面間共享狀態(tài)
LocalStorage在場景使用過程中包含了兩個裝飾器,即@LocalStorageLink 和 @LocalStorageProp
import { TestChild } from './TestChild';
// 創(chuàng)建新實例并使用給定對象初始化
let storage = new LocalStorage({ 'PropA': 47 });
// 使LocalStorage可從@Component組件訪問
@Entry(storage)
@Component
struct Index {
// @LocalStorageLink變量裝飾器與LocalStorage中的'PropA'屬性建立雙向綁定
@LocalStorageLink('PropA') count: number = 1;
build(){
Row(){
Column( {space : 20} ){
Text('混沌 ' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.count++
})
TestChild()
}.width('100%')
}.height('100%')
}
}
復制
@Component
export struct TestChild {
// @LocalStorageLink變量裝飾器與LocalStorage中的'PropA'屬性建立雙向綁定
@LocalStorageLink('PropA') count: number = 1;
build() {
Column( {space : 20} ) {
Text('HarmonyOS - Child' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('TestChild2 更新文字內容')
.onClick( ()= >{
this.count++
})
}.width('100%')
.backgroundColor(Color.Pink)
}
}
復制
總結,本例展示了:
- 使用構造函數(shù)創(chuàng)建LocalStorage實例storage
- 使用@Entry裝飾器將storage添加到 Index 頂層組件中
- @LocalStorageLink綁定LocalStorage對給定的屬性,建立雙向數(shù)據(jù)同步
import { TestChild } from './TestChild';
// 創(chuàng)建新實例并使用給定對象初始化
let storage = new LocalStorage({ 'PropA': 47 });
// 使LocalStorage可從@Component組件訪問
@Entry(storage)
@Component
struct Index {
// @LocalStorageLink變量裝飾器與LocalStorage中的'PropA'屬性建立雙向綁定
@LocalStorageProp('PropA') count: number = 1;
build() {
Row() {
Column( {space : 20} ) {
Text('混沌 ' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('Parent 更新文字內容')
.onClick( ()= >{
this.count++
})
TestChild()
}.width('100%')
}.height('100%')
}
}
復制
let storage = LocalStorage.GetShared()
@Component
export struct TestChild{
// @LocalStorageLink變量裝飾器與LocalStorage中的'PropA'屬性建立雙向綁定
@LocalStorageLink('PropA') count: number = 1;
build() {
Column( {space : 20} ) {
Text('HarmonyOS - Child' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('TestChild2 更新文字內容')
.onClick( ()= >{
this.count++
})
}.width('100%')
.backgroundColor(Color.Pink)
}
}
復制
總結
@LocalStorageLink(key)是和LocalStorage中key對應的屬性建立雙向數(shù)據(jù)同步:
- 本地修改發(fā)生,該修改會被寫回LocalStorage中;
- LocalStorage中的修改發(fā)生后,該修改會被同步到所有綁定LocalStorage對應key的屬性上,包括單向(@LocalStorageProp和通過prop創(chuàng)建的單向綁定變量)、雙向(@LocalStorageLink和通過link創(chuàng)建的雙向綁定變量)變量。
這個例子中TestChild組件使用了@LocalStorageLInk, 當其值發(fā)生變化時,會同時影響到父布局使用到 @LocalStorageProp 裝飾器的變量值,即 子組件的變量通過LocalStorage可以影響到相應的父組件變量值,但父組件的相關變量值是無法影響到子組件的變量值
@StorageLink @StorageProp
AppStorage是應用全局的UI狀態(tài)存儲,是和應用的進程綁定的,由UI框架在應用程序啟動時創(chuàng)建,為應用程序UI狀態(tài)屬性提供中央存儲。
AppStorage在場景使用過程中包含了兩個裝飾器,即@StorageLink 和 @StorageProp
和AppStorage不同的是,LocalStorage是頁面級的,通常應用于頁面內的數(shù)據(jù)共享。而AppStorage是應用級的全局狀態(tài)共享,還相當于整個應用的“中樞”,持久化數(shù)據(jù)PersistentStorage和環(huán)境變量Environment都是通過和AppStorage中轉,才可以和UI交互。
NOTE: AppStorage 和 LocalStorage是互不影響的
import { TestChild } from './TestChild';
AppStorage.SetOrCreate('PropA', 47);
// 創(chuàng)建新實例并使用給定對象初始化
let storage = new LocalStorage();
// 使LocalStorage可從@Component組件訪問
@Entry(storage)
@Component
struct Index {
// @LocalStorageLink變量裝飾器與LocalStorage中的'PropA'屬性建立雙向綁定
@StorageLink('PropA') count: number = 1;
@LocalStorageLink('PropA') countL: number = 1;
build() {
Row(){
Column( {space : 20} ) {
Text('AppStorage ' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('更新AppStorage內容')
.onClick( ()= >{
this.count++
})
Text('LocalStorage ' + this.countL)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('更新LocalStorage內容')
.onClick( ()= >{
this.countL++
})
TestChild()
}.width('100%')
}.height('100%')
}
}
復制
@Component
export struct TestChild {
// @LocalStorageLink變量裝飾器與LocalStorage中的'PropA'屬性建立雙向綁定
@StorageLink('PropA') count: number = 1;
build(){
Column( {space : 20} ) {
Text('HarmonyOS - Child' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('TestChild2 更新文字內容')
.onClick( ()= >{
this.count++
})
}.width('100%')
.backgroundColor(Color.Pink)
}
}
復制
@Builder
@Builder 用于UI元素復用,開發(fā)者可以將重復使用的UI元素抽象成一個方法,在build方法里調用
總結
值引用方式,可以感知父組件的狀態(tài)變化
值傳遞方式,無法感知父組件的狀態(tài)變化
@Entry
@Component
struct Index {
@State count: number = 1;
@Builder BuilderOne($$: { paramA1: number }) {
Column() {
Text(`組件1值引用: ${$$.paramA1} `).fontSize(20)
}.width('100%').backgroundColor(Color.Pink)
}
@Builder BuilderTwo(paramA1: number) {
Column() {
Text(`組件2值傳遞: ${paramA1} `).fontSize(20)
}.width('100%').backgroundColor(Color.Pink)
}
build() {
Row() {
Column({ space: 20 }) {
Text('混沌 ' + this.count)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Button('更新')
.onClick(() = > {
this.count++
})
this.BuilderOne({ paramA1: this.count })
this.BuilderTwo(this.count)
}.width('100%')
}.height('100%')
}
}
復制
@BuilderParam
當開發(fā)者創(chuàng)建了自定義組件,并想對該組件添加特定功能時,例如在自定義組件中添加一個點擊跳轉操作。若直接在組件內嵌入事件方法,將會導致所有引入該自定義組件的地方均增加了該功能。為解決此問題,ArkUI引入了@BuilderParam裝飾器,@BuilderParam用來裝飾指向@Builder方法的變量,開發(fā)者可在初始化自定義組件時對此屬性進行賦值,為自定義組件增加特定的功能。該裝飾器用于聲明任意UI描述的一個元素,類似slot占位符。
import Prompt from '@system.prompt';
import { TestChild } from './TestChild';
@Entry
@Component
struct Index {
@Builder BuilderOne() {
TestChild( {msg: 'BuilderOne 視圖'} ) {
Text('1').fontColor(Color.Red)
}
}
@Builder BuilderTwo() {
Stack(){
TestChild( {msg: 'BuilderTwo 視圖'} ) {
Text('1').fontColor(Color.Red)
Text('2').fontColor(Color.Red)
}
}.onClick( () = > {
Prompt.showToast({message: '點了 BuilderTwo'})
})
}
@BuilderParam aBuilder0: () = > void = this.BuilderOne
@BuilderParam aBuilder1: () = > void = this.BuilderTwo
build(){
Column({ space: 20 }) {
this.aBuilder0()
this.aBuilder1()
TestChild( {msg: '中國'} ) {
Text('1').fontColor(Color.Red)
})
}.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
復制
@Component
export struct TestChild {
msg: string
@BuilderParam aB0: () = > {}
build(){
Column( {space : 20} ) {
this.aB0()
Text('TestChild上下有 '+ this.msg)
.fontSize(20)
.fontWeight(FontWeight.Bold)
this.aB0()
}.width('100%')
.backgroundColor(Color.Pink)
}
}
復制
總結
- @BuilderParam 既可以指向一個對象, 也可以指向@Builder修飾的方法
- 關于子組件占位出現(xiàn)兩個的問題,應該是系統(tǒng)原因
- 帶占位的自定義視圖是沒法響應onClick事件的,所以在本示例種,將子組件外邊再添加了一個容器組件,用來進行點擊事件響應
@Styles
如果每個組件的樣式都需要單獨設置,在開發(fā)過程中會出現(xiàn)大量代碼在進行重復樣式設置,雖然可以復制粘貼,但為了代碼簡潔性和后續(xù)方便維護,我們推出了可以提煉公共樣式進行復用的裝飾器@Styles
import Prompt from '@system.prompt';
@Entry
@Component
struct Index {
//僅支持公共屬性
@Styles fancy() {
.width(200)
.height(300)
.backgroundColor(Color.Pink)
.onClick(() = > {
Prompt.showToast({message: 'I am fancy'})
})
}
build() {
Column({ space: 20 }) {
Text('Styles')
.textAlign(TextAlign.Center)
.fancy()
}.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
復制
總結
- @Styles 當前僅支持通用屬性
- @Styles 修飾的方法不支持參數(shù)
- 引用@Styles 修飾的方法時,建議放在最后,比如:Text().fancy().textAlign(....) 應該變?yōu)?Text().textAlign(....) .fancy()
@Extend
用于擴展原生組件樣式
注意
- 原生指的ArkTS寫的組件
- 擴展,不是新定義增加不存在的屬性
import Prompt from '@system.prompt';
//僅支持公共屬性
@Styles function fancy() {
.width(200)
.height(300)
.backgroundColor(Color.Pink)
.onClick(() = > {
Prompt.showToast({message: 'I am fancy'})
})
}
@Extend(Text) function superFancy(size:number, onClick?: () = > void) {
.fontSize(size)
.textAlign(TextAlign.Center)
.fancy()
.onClick(onClick)
}
@Entry
@Component
struct Index {
onClickHandler() {
Prompt.showToast({message: 'fancy出去了'})
}
build(){
Column({ space: 20 }) {
Text('Styles')
.superFancy(30, this.onClickHandler.bind(this))
}.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
復制
總結
- @Extend 在 @Styles基礎上,增加了傳參特性
- @Extend 必須定義為全局
- 支持封裝指定的組件的私有屬性和私有事件和預定義相同組件的@Extend的方法
@Concurrent
在使用TaskPool時,執(zhí)行的并發(fā)函數(shù)需要使用該裝飾器修飾,否則無法通過相關校驗。
import taskpool from '@ohos.taskpool';
@Concurrent
function add(num1: number, num2: number): number {
return num1 + num2;
}
async function ConcurrentFunc(): Promise< void > {
try {
let task: taskpool.Task = new taskpool.Task(add, 1, 2);
console.info("taskpool res is: " + await taskpool.execute(task));
}catch (e) {
}
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build(){
Row(){
Column(){
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() = > {
ConcurrentFunc();
})
}.width('100%')
}.height('100%')
}
}
可+mau123789記住是v喔!
結尾
到此我們已學完所有的裝飾器用法,靈活使用裝飾器,全憑官方指導文檔是不夠的,它僅僅提供了一種最小化的場景使用模型,到了具體業(yè)務實現(xiàn)場景中,非常容易犯糊涂蒙圈。可以前往參考這個鴻蒙技術文檔qr23.cn/AKFP8k
。
個人感覺@BuilderParam和 @ObjectLink理解起來還是有點費勁。
-
移動開發(fā)
+關注
關注
0文章
52瀏覽量
9809 -
鴻蒙
+關注
關注
57文章
2388瀏覽量
42964 -
HarmonyOS
+關注
關注
79文章
1980瀏覽量
30401 -
OpenHarmony
+關注
關注
25文章
3744瀏覽量
16473 -
鴻蒙OS
+關注
關注
0文章
190瀏覽量
4488
發(fā)布評論請先 登錄
相關推薦
評論