目录
- 渲染控制概述
- if/else:条件渲染
- ForEac:循环渲染
- LazyForEach:数据懒加载
- Repeat:循环渲染(推荐)
- ConnentSlot:混合开发
渲染控制概述
ArkUI通过自定义组件的build()函数和@builder装饰器声明式UI描述语句构建相应的的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件语句,基于数组数据快速生成组件的虚幻渲染语句,针对大数据量的数据懒加载语句,针对混合模式开发的组件渲染语句。
if/else:条件渲染
ArkTS提供了渲染控制的能力,条件渲染可根据应用的不同状态,使用if、else和else if渲染对应状态下的UI内容。
使用规则
- 支持if、else和else if语句
- if、else后跟随的条件语句可以使用状态变量
- 允许在容器组件内使用,通过条件渲染语句构建不同的子组件。
- 条件渲染语句在涉及到组件的父子关系时是"透明"的,当父组件和子组件之间存在一个或多个if语句时,必须遵守父组件关于子组件使用的规则
- 每个分支内部的构建海曙必须遵循构建函数的规则,并创建一个或多个组件。无法创建组建的空构建函数会产生语法错误
- 某些容器组件限制子组件的类型或数量,将条件渲染语句用于这些组件内时,这些限制同样应用于条件渲染语句内创建的组件。例如:Grid容器组件的子组件仅支持GridItem组件,在Grid内使用条件渲染语句时,条件渲染语句内仅允许使用GridItem组件。
更新机制
当if、else后跟随的状态判断中使用的状态变量值变化时,条件渲染语句会进行更新,更新步骤如下:
- 评估if和else if的状态判断条件,如果分支没有变化,无需执行以下步骤。如果分支有变化,则执行2、3步骤
- 删除此前构建的所有子组件
- 执行新分支的构造函数,将获取到的组件添加到if父容器中。如果缺少适用的else分支,则不构建然和内容。 条件可以包括Typescript表达式。对于构造函数中的表达式,此类表达式不得更改应用程序状态。
ForEach:循环渲染
ForEach接口基于数组类型数据来进行需=循环渲染,需要与容器组件配合使用,且接口返回的数组应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为List组件
接口 描述
ForEach(
arr: Array,
itemGenerator: (item: Object, index: number) => void,
keyGenerator?: (item: Object, index: number) => string
)
以下是参数的详细说明:
键值生成规则
在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于表示对应的组件。当这个键值发生变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。
ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数。
ArkUI框架对于ForEach的键值生成有一套特定的判断规则,这主要与itemGenerator函数的第二个参数index以及keyGenerator函数的第二个参数index有关,具体的键值生成规则判断逻辑如下图:
LazyForEach:数据懒加载
LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件划出可视区域外时,框架会进行组件销毁回收以降低内存占用。
接口描述
LazyForEach(
dataSource:IDataSource, //需要进行数据迭代的数据源
itemGenerator:(item:Object,index:number) => void,
keyGenerator:(item:Object,index:number) => string
):void
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
dataSource | IDataSource | 是 | LazyForEach数据源,需要开发者实现相关接口 |
itemGenerator | (item:Objec,index:number) => void | 是 | 子组件生成函数,为数组中的每一个数据项创建一个子组件。 |
keyGenerator | (item:Object,index:number) =>string | 否 | 简直生成函数,用于给数据源中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能更好的识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 |
IDataSource类型说明
interface IDataSource {
totalCount():number; //获得数据总数
getData(index:number):Object; //获取索引值对应的数据
registerDataChangeListener(listener:DataChangeListener):void; //注册数据改变的监听器
unregisterDataChangeListener(listener:DataChangeListener):void; //注销数据改变的监听器
}
DataChangeListener类型说明
interface DataChangeListener {
onDataReloaded():void; //重新加载数据完成调用
onDataAdded(index:number):void; //添加数据完成后调用
onDataMoved(from:number,to:number):void; //数据移动起始位置与数据移动目标位置交换完成后调用
onDataDeleted(index:number):void; //删除数据完成后调用
onDataChaged(index:number):void; //改变数据完成后调用
onDataAdd(index:number):void; //添加数据完成后调用
onDataMove(from:number,to:number):void; //数据移动起始位置与数据移动目标位置交换完成后调用
onDataDelete(index:number):void; //删除数据完成后调用
onDataChange(index:number):void; //改变数据完成后调用
onDatasetChange(dataOperations:DataOperation[]):void; //批量数据处理后调用
}
DataOperation类型说明
type DataOperation =
DataAddOperation | //添加数据操作
DataDeleteOperation | //删除数据操作
DataChangeOperation | //改变数据操作
DataMoveOperation | //移动数据操作
DataExchangeOperation | //交换数据操作
DataReloadOperation //重载所有数据操作
DataAddOperation
interface DataAddOperation {
type:DataOperationType.ADD, //数据添加类型
index:number, //插入数据索引值
count?:number, //插入数量,默认值为1
ket?:string|Array<string> //为插入的数据分配键值
}
DataDeleteOperation
interface DataDeleteOperation{
type:DataOperationType.DELETE, //数据删除类型
index:number, //起始删除位置索引值
count?:number //删除数据数量,默认值为1
}
DataChangeOperation
interface DataChangeOperation{
type:DataOperationType.CHANGE, //数据改变类型
index:number, //改变的数据的索引值
key?:string //为改变的数据分配新的键值,默认使用原键值
}
DataMoveOperation
interface MoveIndex {
from:number; //起始移动位置
to:number; //目的移动位置
}
interface DataMoveOperation {
type:DataOperationType.MOVE, //数据移动类型
index:MoveIndex,
key?:string //为被移动的数据分配新的键值,默认使用原键值
}
DataExchangeOperation
interface ExchangeIndex {
start:number; //第一个交换位置
end:number; //第二个交换位置
}
interface ExchangeKey {
start:string; //为第一个交换的位置分配新的键值,默认使用原键值
end:string; //为第二个交换的位置分配新的键值,默认使用原键值
}
interface DataExchangeOperation {
type:DataOperationType.EXCHANGE, //数据交换类型
index:ExchangeIndex,
key?:ExchangeKey
}
DataReloadOperation
interface DataReloadOperation { //当onDataChange含有DataOperationType.RELOAD操作时,其余操作全部失效,框架会自己调用keyGenerator进行键值比对
type:DataOperationType.RELOAD //数据全部重新加载
}
使用限制
- LazyForEach必须在容器组件内使用,仅有List、Grid、Swiper以及WaterFlow组件支持数据懒加载(可配置cachedCount属性,即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有数据。
- LazyForEach在每次迭代中,必须创建且只允许创建一个子组件
- 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件
- 允许LazyForEach包含在if/else条件渲染语句中,也允许LazyForEach中出现if/else条件渲染语句
- 键值生成器必须针对每个数据生成唯一的值,如果键值相同,将导致键值相同的UI组件渲染出问题
- LazyForEach必须使用DataChangeListener对象来进行更新,第一个参数dataSource使用状态变量时,状态变量改变不会触发LazyForEach的UI刷新
- 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,需要生成不同于原来的键值来触发组件刷新
键值生成规则
在LazyForEach循环渲染过程中,系统会为每个item生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。
LazyForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即(item: Object, index: number) => { return viewId + ‘-’ + index.toString(); }, viewId在编译器转换过程中生成,同一个LazyForEach组件内其viewId是一致的。
Repeat:循环渲染(推荐)
Repeat组件基于数组类型数据进行循环渲染,需要与容器组件配合使用,且接口返回的数组应当是允许包含在Repeat父容器组件中的子组件。
Repeat循环渲染和ForEach相比有两个区别,一是优化了部分更新场景下的渲染性能,二是组件生成函数的索引index由框架侧来维护。
接口描述
Repeat 组件构造
declare const Repeat:<T>(arr:Array<T>) => RepeatAttribute<T>
参数说明:
参数名 | 参数类型 | 是否必填 | 参数描述 |
---|---|---|---|
arr | Array<T > | 是 | 数据源,为Array<T >类型的数组,由开发者决定参数类型。 |
Repeat组件属性
declare class RepeatAttribute<T> {
each(itemGenerator:(repeatItem:RepeatItem<T>) => void) : RepeatAttribute<T>;
key(keyGenerator:(item:T,index:number) => string) : RepeatAttribute<T>;
}
参数说明:
RepeatItem 类型
interface RepeatItem<T> {
item:T,
index?:number
}
参数说明:
参数名 | 参数类型 | 是否必填 | 参数描述 |
---|---|---|---|
item | T | 是 | arr中每一个数据项。T为开发者传入的数据类型 |
number | number | 否 | 当前数据项对应的索引 |
键值生成规则
在Repeat循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,会基于此去做对应的更新。若当前数组有数据项对应的键值与其他项重复,Repeat会为每一项数据补上index作为新的键值。
Repeat提供了key属性,参数是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有调用key属性,则ArkUI框架会使用默认的键值生成函数。
ArkUI框架对于Repeat的键值生成有一套特定的判断规则,具体的键值生成规则判断逻辑如下图所示。
ContentSlot: 混合开发
用于渲染并管理Native层使用C-API创建的组件。
支持混合模式开发,当容器是ArkTS组件,子组件在Native侧创建时,推荐使用ContentSlot占位组件。
接口
ContentSlot组件接口
//当前开发者需要使用ArkUI提供的NodeContent作为管理器
ContentSlot(content:Content)
参数名 | 类型 | 必须 | 参数描述 |
---|---|---|---|
content | Content | 是 | Content作为ContentSlot的管理器,通过Native侧提供的接口,可以注册并触发ContentSlot的上下树事件回调以及管理ContentSlot的子组件 |
abstract class Content {
}
ContentSlotInterface
(content:Content):ContentSlotAttribute;
当内容添加到占位符组件时调用
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
参数名 | 类型 | 必须 | 参数描述 |
---|---|---|---|
content | Content | 是 | Content作为ContentSlot的管理器,通过Native侧提供的接口,可以注册并触发ContentSlot的上下树事件回调以及管理ContentSlot的子组件 |
ContentSlotAttribute
定义ContentSlot属性,以防止不正确的递归使用ContentSlot
系统能力: SystemCapability.ArkUI.ArkUI.Full
Native侧接口
接口名 | 描述 |
---|---|
OH_ArkUI_NodeContent_RegisterCallback(ArkUI_NodeContentHandle content, ArkUI_NodeContentCallback callback) | 向管理器Content上注册事件。 |
OH_ArkUI_NodeContentEvent_GetEventType(ArkUI_NodeContentEvent* event) | 获取Content上触发的事件类型。 |
OH_ArkUI_NodeContent_AddNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node) | 在Content上添加子组件。 |
OH_ArkUI_NodeContent_InsertNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node, int32_t position) | 在Content上插入子组件。 |
OH_ArkUI_NodeContent_RemoveNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node) | 在Content上移除子组件。 |
OH_ArkUI_GetNodeContentFromNapiValue(napi_env env, napi_value value, ArkUI_NodeContentHandle* content) | 在Native侧获取ArkTS侧Content指针。 |
typedef enum {NOTE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW = 0,NOTE_CONTENT_EVENT_ON_DETACH_FROM_WINDOW = 1,} ArkUI_NodeContentEventType | Content上会触发的上树和下树两种事件类型。 |
写在最后
●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
●更多鸿蒙最新技术知识点,请移步前往小编:https://gitee.com/