HarmonyOS 4.0 应用开发快速入门之 04-组件进阶
AI 摘要
组件状态进阶
对象类型状态
当 @State 装饰的是一个普通的对象类型时,@State 能监听到数据的变化。
示例:对象类型的状态监听
Index 组件(src/main/ets/pages/Index.ets):
// 定义对象模型
class User {
name: string
age: number
}
@Entry
@Component
struct Index {
// 实例化User对象
@State
user: User = {
name: '张三',
age: 18
}
build() {
Row() {
Column({space: 10}) {
Text('姓名:' + this.user.name)
Text('年龄:' + this.user.age)
Button('age++')
.onClick(() => {
this.user.age++
})
}
.width('100%')
}
.padding(20)
}
}示例效果:
嵌套对象类型状态
当 @State 装饰的是一个嵌套的对象类型时,@State 无法监听到嵌套的数据的变化。
示例:嵌套对象类型的状态监听
Index 组件(src/main/ets/pages/Index.ets):
// 定义对象模型
class Role {
roleName: string
}
class User {
name: string
age: number
// User嵌套Role
role: Role
}
@Entry
@Component
struct Index {
// 实例化User对象
@State
user: User = {
name: '张三',
age: 18,
role: {
roleName: '技术部'
}
}
build() {
Row() {
Column({space: 10}) {
Text('姓名:' + this.user.name)
Text('年龄:' + this.user.age)
Text('部门名称:' + this.user.role.roleName)
Button('age++')
.onClick(() => {
this.user.age++
})
Button('role change')
.onClick(() => {
// 在JavaScript中引用数据类型,变量保存的值是引用地址
// 直接修改this.user.role.roleName导致user.role的内存地址未变化
// @State无法监测到数据的变化
// this.user.role.roleName = '综合部'
// 将旧的role对象复制到一个新对象
// ...符号表示拷贝
const role = {...this.user.role}
// 修改roleName
role.roleName = '综合部'
// 将新的role对象赋值给user对象
this.user.role = role
})
}
.width('100%')
}
.padding(20)
}
}示例效果:
对象数组类型状态
当 @State 装饰的是一个对象数组类型时,@State 无法监听到数组内部的数据的变化。
示例:对象数组类型的状态监听
Index 组件(src/main/ets/pages/Index.ets):
class User {
name: string
age: number
}
@Entry
@Component
struct Index {
// 实例化User对象数组
@State
userList: User[] = [
{ name: '张三', age: 18 },
{ name: '李四', age: 19 },
{ name: '王五', age: 20 }
]
build() {
Row() {
Column({space: 10}) {
Text('姓名:' + this.userList[0].name)
Text('年龄:' + this.userList[0].age)
Button('age++')
.onClick(() => {
const user = {...this.userList[0]}
user.age++
this.userList[0] = user
})
}
.width('100%')
}
.padding(20)
}
}示例效果:
界面渲染
条件渲染
条件渲染可根据应用的不同状态,使用 if、else 和 else if 渲染对应状态下的 UI 内容。
- 条件渲染是根据状态数据进行判断展示不同 UI。
- 条件渲染时会销毁和创建组件(包括自定义组件和内置组件),组件状态将不会保留。
示例:条件渲染
Index 组件(src/main/ets/pages/Index.ets):
@Entry
@Component
struct Index {
// 是否显示加载动效组件
@State
loading: boolean = true
build() {
Row() {
Column({space: 10}) {
// 按钮点击切换加载动效
Button('change')
.onClick(() => {
this.loading = !this.loading
})
// 条件判断
if(this.loading) {
LoadingProgress()
} else {
Text('Hello HarmonyOS')
// 条件渲染会销毁和创建组件,因此切换时输入框的数据不会被保留
TextInput()
}
}
.width('100%')
}
.height('100%')
}
}示例效果:
循环渲染
ForEach 基于数组类型数据来进行循环渲染,需要与容器组件配合使用。
ForEach 的基本用法:
ForEach(
arr: Array,
itemGenerator: (item: any, index?: number) => void,
keyGenerator?: (item: any, index?: number) => string
)ForEach 的基本参数:
| 名称 | 类型 | 描述 |
|---|---|---|
| arr | array | 要遍历的列表 |
| itemGenerator | (item: any, index?: number) => void | 组件生成函数 item:遍历后每个数据项的别名 index:数据下标索引 |
| keyGenerator | (item: any, index?: number) => string | 键值生成函数,为数据源 arr 的每个数组项生成唯一且持久的键值 |
示例:循环渲染
Index 组件(src/main/ets/pages/Index.ets):
class User {
name: string
age: number
}
@Entry
@Component
struct Index {
// 实例化User对象数组
@State
userList: User[] = [
{ name: '张三', age: 18 },
{ name: '李四', age: 19 },
{ name: '王五', age: 20 },
{ name: '唐三藏', age: 21 },
{ name: '孙悟空', age: 22 },
{ name: '猪八戒', age: 23 },
{ name: '沙悟净', age: 24 }
]
build() {
Row() {
Column({space: 10}) {
ForEach(this.userList, (item: User, index: number) => {
Text('id is ' + index + ', name is ' + item.name + ', age is ' + item.age)
})
}
.width('100%')
}
.padding(20)
}
}示例效果:
自定义构建函数
@Builder
参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-builder-0000001524176981-V2
ArkUI 还提供了一种更轻量的 UI 元素复用机制 @Builder,@Builder 所装饰的函数遵循 build() 函数语法规则,开发者可以将重复使用的 UI 元素抽象成一个方法,在 build 方法里调用。
自定义构建函数支持全局定义和组件内定义。
示例:自定义构建函数
Index 组件(src/main/ets/pages/Index.ets):
// 全局定义自定义构建函数
@Builder
function publicBuilderFunction() {
Text('查看更多 >>')
.fontColor(Color.Gray)
}
@Entry
@Component
struct Index {
// 组件内定义自定义构建函数
@Builder
myBuilderFunction() {
Text('查看更多 >>')
.fontColor(Color.Gray)
}
build() {
Column(){
Row() {
Text('评价 (2000)')
.layoutWeight(1)
.fontWeight(FontWeight.Bold)
// 调用组件内自定义构建函数
this.myBuilderFunction()
}
.padding(10)
Row() {
Text('推荐')
.layoutWeight(1)
// 调用全局自定义构建函数
publicBuilderFunction()
}
.padding(10)
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#ffeaeaea')
}
}示例效果:
传递参数
自定义构建函数的参数传递有按值传递和按引用传递两种。
按值传递:调用 @Builder 装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起 @Builder 方法内的 UI 刷新。
示例:按值传递参数
Index 组件(src/main/ets/pages/Index.ets):
@Entry
@Component
struct Index {
// 组件内定义自定义构建函数
@Builder
myBuilderFunction(title: string) {
Text(title)
.fontColor(Color.Gray)
}
build() {
Column(){
Row() {
Text('评价 (2000)')
.layoutWeight(1)
.fontWeight(FontWeight.Bold)
// 调用组件内自定义构建函数
// 传递title参数
this.myBuilderFunction('查看更多 >>')
}
.padding(10)
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#ffeaeaea')
}
}示例效果:
按引用传递:传递的参数可为状态变量,且状态变量的改变会引起 @Builder 方法内的 UI 刷新。
示例:按引用传递参数
Index 组件(src/main/ets/pages/Index.ets):
@Entry
@Component
struct Index {
@State
rate: number = 0
// 组件内定义自定义构建函数
// $$:按引用传递参数的范式
@Builder
myBuilderFunction($$: {title: string}) {
Text($$.title)
.fontColor(Color.Gray)
}
build() {
Column(){
Row() {
Text('评价 (2000)')
.layoutWeight(1)
.fontWeight(FontWeight.Bold)
// 调用组件内自定义构建函数
// 引用传递title参数
// this.rate变化时,UI变化
this.myBuilderFunction({title: 'rate is ' + this.rate})
}
.padding(10)
Row() {
Button('rate++')
.onClick(() => {
this.rate++
})
}
.padding(10)
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#ffeaeaea')
}
}示例效果: