# 20.3:自定义组件传值浅析

笔者在第 9 章自定义组件系列文章中讲述了组件的生命周期以及如何自定义一个组件,并介绍了如何给自定义组件传递参数的内容,本节笔者简单介绍一下向自定义组件传递参数的实现原理。

# 20.3.1:向自定义组件传值

根据第九章的内容讲解,笔者先简单实现一个自定义组件 CustomText 并给其定义相关属性,代码如下所示:

// 自定义一个组件
@Component struct CustomText {

  text: string = "ArkUI实战";
  textWidth: number = 220;
  textHeight: number = 50;

  build() {
    Text(this.text)
      .width(this.textWidth)
      .height(this.textHeight)
      .textAlign(TextAlign.Center)
      .backgroundColor("#aabbcc")
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

CustomText 定义了 3 个属性,各属性说明如下:

  • text:表示待显示的文本内容,默认值为""。
  • textWidth:表示自定义组件的宽度,默认值为 120 vp。
  • textHeight:表示自定义组件的高度,默认值为 50 vp。

使用自定义组件,代码如下:

@Entry @Component struct ArkUIClubTest {
  build() {
    Column({space: 10}) {
      CustomText()                  // 使用自定义组件,没有传递参数则使用默认值

      CustomText({                  // 使用自定义组件,用{}表示传递参数
        text: "Hello, OpenHarmony", // 参数和 CustomText 内的参数一一对应
        textWidth: 250,
        textHeight: 55
      })
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

样例中,声明了两次 CustomText,第一个没有传递参数,第二个传递了参数。传递参数的做法是:在 CustomText 的构造方法内传递一个匿名对象,该匿名对象使用 {} 表示并且匿名对象的属性名称和类型与 CustomText 内的属性一一对应,这样就完成了向自定义组件传值的操作。样例代码运行效果如下所示:

20_3_1_1

# 20.3.2:反编译字节码文件

根据前两篇文章的介绍,ArkTS 代码最终被编译器编译成对应的 abc 字节码,使用 010 Editor 打开生成的字节码文件,部分内容如下所示:

(() => {
class ArkUIClubTest extends View {
    // 构造方法
    constructor(compilerAssignedUniqueChildId, parent, params, localStorage) {
        super(compilerAssignedUniqueChildId, parent, localStorage);
        this.updateWithValueParams(params);
    }
    updateWithValueParams(params) {
    }
    aboutToBeDeleted() {
        SubscriberManager.Get().delete(this.id());
    }
    render() {
        // Column 表示ArkUIClubTest的第一个子组件
        Column.create({ space: 10 });
        Column.width('100%');
        Column.height('100%');
        Column.padding(10);
        // earlierCreatedChild_2表示第二个子组件,2 是子组件的顺序
        // 如果定义了findChildById()方法则执行,否则赋值undefined
        let earlierCreatedChild_2 = (this && this.findChildById) ? this.findChildById("2") : undefined;
        if (earlierCreatedChild_2 == undefined) {
            // earlierCreatedChild_2没有找到,就新创建一个CustomText
            // 因为第一个 CustomText 未给其传递参数,因此使用{}表示
            View.create(new CustomText("2", this, {}));
        } else {
            // 已经创建过earlierCreatedChild_2,这调用其updateWithValueParams方法
            earlierCreatedChild_2.updateWithValueParams({});
            if (!earlierCreatedChild_2.needsUpdate()) {
                earlierCreatedChild_2.markStatic();
            }
            View.create(earlierCreatedChild_2);
        }
        // earlierCreatedChild_3表示ArkUIClubTest的第3个子组件
        let earlierCreatedChild_3 = (this && this.findChildById) ? this.findChildById("3") : undefined;
        if (earlierCreatedChild_3 == undefined) {
            // earlierCreatedChild_3没有找到,就新创建一个CustomText
            // 因为第二个 CustomText 给其传递参数了,所以{}内是有值的
            View.create(new CustomText("3", this, {
                text: "Hello, OpenHarmony",
                textWidth: 250,
                textHeight: 55
            }));
        } else {
            // 已经创建过earlierCreatedChild_2,这调用其updateWithValueParams方法
            earlierCreatedChild_3.updateWithValueParams({
                text: "Hello, OpenHarmony",
                textWidth: 250,
                textHeight: 55
            });
            if (!earlierCreatedChild_3.needsUpdate()) {
                earlierCreatedChild_3.markStatic();
            }
            View.create(earlierCreatedChild_3);
        }
        Column.pop();
    }
}

class CustomText extends View {
    /**
     * compilerAssignedUniqueChildId: 当前组件在父组件中的唯一id
     * parent:当前组件的父组件
     * params:传递给当前组件的参数
     * localStorage:数据缓存相关
     */
    constructor(compilerAssignedUniqueChildId, parent, params, localStorage) {
        super(compilerAssignedUniqueChildId, parent, localStorage);
        this.text = "ArkUI实战";              // 默认值
        this.textWidth = 220;                // 默认值
        this.textHeight = 50;                // 默认值
        this.updateWithValueParams(params);  // 调用updateWithValueParams方法更新属性值
    }
    
    /**
     * 更新组件参数
     */
    updateWithValueParams(params) {
        if (params.text !== undefined) {     // 如果传递了有效的text参数则使用
            this.text = params.text;
        }
        if (params.textWidth !== undefined) {// 如果传递了有效的textWidth参数则使用
            this.textWidth = params.textWidth;
        }
        if (params.textHeight !== undefined) {// 如果传递了有效的textHeight参数则使用
            this.textHeight = params.textHeight;
        }
    }
    aboutToBeDeleted() {
        SubscriberManager.Get().delete(this.id());
    }
    render() {
        Text.create(this.text);
        Text.width(this.textWidth);
        Text.height(this.textHeight);
        Text.textAlign(TextAlign.Center);
        Text.fontSize(24);
        Text.backgroundColor("#aabbcc");
        Text.pop();
    }
}
loadDocument(new ArkUIClubTest("1", undefined, {}));
})()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

反编译后的 abc 代码笔者注释的很清晰,首先 @Component 修饰的组件都被编译成了继承自 View,其次它们都有构造方法,参数都是通过构造方法传递进来的并在 updateWithValueParams() 方法中给属性赋值,因此在程序后续执行过程中可以直接使用传递过来的值了,是不是很简单……(#^.^#)

# 20.3:小结

本节笔者简单讲解了一下向自定义组件传递参数的实现过程,本质上就是深刻理解 struct 的作用,ArkUI 开发框架中的组件都是基于 struct 实现的并且不允许有继承关系,对于 struct 的实例化,可以省略 new 关键字。

请作者喝杯咖啡

津公网安备 12011402001367号

津ICP备2020008934号-2

中央网信办互联网违法和不良信息举报中心

天津市互联网违法和不良信息举报中心