diff --git a/zh-cn/application-dev/ui/ndk-node-query-operate.md b/zh-cn/application-dev/ui/ndk-node-query-operate.md index 6b24c646481bea9096ff8207ab2b4b9bdcea54f5..9f8f94cbaf0088b09a1fa4716511bb1841b81f36 100644 --- a/zh-cn/application-dev/ui/ndk-node-query-operate.md +++ b/zh-cn/application-dev/ui/ndk-node-query-operate.md @@ -6,10 +6,40 @@ NDK提供一系列节点查询、遍历、操作能力,通过使用以下接 uniqueId是系统分配的唯一标识的节点Id。 -从API version 20开始,使用[OH_ArkUI_NodeUtils_GetNodeUniqueId](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getnodeuniqueid)接口,可以获取目标节点的uniqueId。 - -从API version 20开始,使用[OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getnodehandlebyuniqueid)接口,可以通过uniqueId获取目标节点的指针。 - +从API version 20开始,使用[OH_ArkUI_NodeUtils_GetNodeUniqueId](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getnodeuniqueid)接口,可以获取目标节点的uniqueId。使用[OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getnodehandlebyuniqueid)接口,可以通过uniqueId获取目标节点的指针。 + +```c++ +testNode = nodeAPI->createNode(ARKUI_NODE_COLUMN); +ArkUI_NumberValue value[] = {480}; +ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)}; +value[0].f32 = 300; +nodeAPI->setAttribute(testNode, NODE_WIDTH, &item); +nodeAPI->setAttribute(testNode, NODE_HEIGHT, &item); +struct IdList { + int32_t id = -1; +}; +IdList *idl = new IdList; +int32_t uid = -1; +OH_ArkUI_NodeUtils_GetNodeUniqueId(testNode, &uid); +idl->id = uid; +auto button = nodeAPI->createNode(ARKUI_NODE_BUTTON); +value[0].f32 = 50; +nodeAPI->setAttribute(button, NODE_WIDTH, &item); +nodeAPI->setAttribute(button, NODE_HEIGHT, &item); +nodeAPI->addChild(testNode, button); +nodeAPI->registerNodeEvent(button, NODE_ON_CLICK, 1, idl); +nodeAPI->registerNodeEventReceiver([](ArkUI_NodeEvent *event) { + auto targetId = OH_ArkUI_NodeEvent_GetTargetId(event); + if (targetId == 1) { + auto idl = (IdList *)OH_ArkUI_NodeEvent_GetUserData(event); + ArkUI_NodeHandle Test_Column; + auto ec = OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId(idl->id, &Test_Column); + if (ec == 0) { + OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "Manager","GetNodeHandleByUniqueId success"); + } + } +}); +``` ## 通过用户id获取节点信息 使用[OH_ArkUI_NodeUtils_GetAttachedNodeHandleById](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getattachednodehandlebyid)接口,可以通过用户设置的id获取目标节点的指针。 @@ -431,4 +461,291 @@ uniqueId是系统分配的唯一标识的节点Id。 4. 运行程序,点击按钮,Stack节点会移动到目标位置。 -![moveToNativeDemo](figures/moveToNativeDemo.gif) \ No newline at end of file +![moveToNativeDemo](figures/moveToNativeDemo.gif) + +## 用不同的展开模式获取对应下标的子节点 + +NDK支持通过不同的展开方式获取目标节点下的有效节点信息。例如,在LazyForEach场景下,可以处理存在多个子节点的情况。 + +从API version 20开始,使用[OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getfirstchildindexwithoutexpand)接口,可以获取目标节点的第一个存在于组件树的节点。使用[OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getlastchildindexwithoutexpand)接口,可以获取目标节点的最后一个存在于组件树的节点。[OH_ArkUI_NodeUtils_GetChildWithExpandMode](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getchildwithexpandmode)接口,可以通过不同的节点展开模式获取对应下标的子节点。 + +> **说明:** +> +> 节点展开方式请参考[ArkUI_ExpandMode](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_expandmode),此处推荐使用ARKUI_LAZY_EXPAND懒展开方式,智能识别对应场景。 + +1. 通过ArkTS构造LazyForEach及ArkTS的展开场景。 + + ```ts + import { NodeController, FrameNode, UIContext, BuilderNode, ExpandMode, LengthUnit } from '@kit.ArkUI'; + + const TEST_TAG: string = "FrameNode "; + + // BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新 + class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: string[] = []; + + public totalCount(): number { + return 0; + } + + public getData(index: number): string { + return this.originDataArray[index]; + } + + // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + // 通知LazyForEach组件需要重载所有子组件 + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }) + } + + // 通知LazyForEach组件需要在index对应索引处添加子组件 + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]); + }) + } + + // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]); + }) + } + + // 通知LazyForEach组件需要在index对应索引处删除该子组件 + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + // 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]); + }) + } + + // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + // 写法2:listener.onDatasetChange( + // [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]); + }) + } + + notifyDatasetChange(operations: DataOperation[]): void { + this.listeners.forEach(listener => { + listener.onDatasetChange(operations); + }) + } + } + + class MyDataSource extends BasicDataSource { + private dataArray: string[] = [] + + public totalCount(): number { + return this.dataArray.length; + } + + public getData(index: number): string { + return this.dataArray[index]; + } + + public addData(index: number, data: string): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + public pushData(data: string): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } + } + + class Params { + data: MyDataSource | null = null; + scroller: Scroller | null = null; + constructor(data: MyDataSource, scroller: Scroller) { + this.data = data; + this.scroller = scroller; + } + } + + @Builder + function buildData(params: Params) { + List({ scroller: params.scroller }) { + LazyForEach(params.data, (item: string) => { + ListItem() { + Column() { + Text(item) + .fontSize(20) + .onAppear(() => { + console.log(TEST_TAG + " node appear: " + item) + }) + .backgroundColor(Color.Pink) + .margin({ + top: 30, + bottom: 30, + left: 10, + right: 10 + }) + } + } + .id(item) + }, (item: string) => item) + } + .cachedCount(5) + .listDirection(Axis.Horizontal) + } + + class MyNodeController extends NodeController { + private rootNode: FrameNode | null = null; + private uiContext: UIContext | null = null; + private data: MyDataSource = new MyDataSource(); + private scroller: Scroller = new Scroller(); + + makeNode(uiContext: UIContext): FrameNode | null { + this.uiContext = uiContext; + for (let i = 0; i <= 20; i++) { + this.data.pushData(`N${i}`); + } + const params: Params = new Params(this.data, this.scroller); + const dataNode: BuilderNode<[Params]> = new BuilderNode(uiContext); + dataNode.build(wrapBuilder<[Params]>(buildData), params); + this.rootNode = dataNode.getFrameNode(); + const scrollToIndexOptions: ScrollToIndexOptions = { + extraOffset: { + value: 20, unit: LengthUnit.VP + } + }; + this.scroller.scrollToIndex(6, true, ScrollAlign.START, scrollToIndexOptions); + return this.rootNode; + } + + getFirstChildIndexWithoutExpand() { + console.log(`${TEST_TAG} getFirstChildIndexWithoutExpand: ${this.rootNode!.getFirstChildIndexWithoutExpand()}`); + } + + getLastChildIndexWithoutExpand() { + console.log(`${TEST_TAG} getLastChildIndexWithoutExpand: ${this.rootNode!.getLastChildIndexWithoutExpand()}`); + } + + getChildWithNotExpand() { + const childNode = this.rootNode!.getChild(3, ExpandMode.NOT_EXPAND); + console.log(TEST_TAG + " getChild(3, ExpandMode.NOT_EXPAND): " + childNode!.getId()); + if (childNode!.getId() === "N9") { + console.log(TEST_TAG + " getChild(3, ExpandMode.NOT_EXPAND) result: success."); + } else { + console.log(TEST_TAG + " getChild(3, ExpandMode.NOT_EXPAND) result: fail."); + } + } + + getChildWithExpand() { + const childNode = this.rootNode!.getChild(3, ExpandMode.EXPAND); + console.log(TEST_TAG + " getChild(3, ExpandMode.EXPAND): " + childNode!.getId()); + if (childNode!.getId() === "N3") { + console.log(TEST_TAG + " getChild(3, ExpandMode.EXPAND) result: success."); + } else { + console.log(TEST_TAG + " getChild(3, ExpandMode.EXPAND) result: fail."); + } + } + + getChildWithLazyExpand() { + const childNode = this.rootNode!.getChild(3, ExpandMode.LAZY_EXPAND); + console.log(TEST_TAG + " getChild(3, ExpandMode.LAZY_EXPAND): " + childNode!.getId()); + if (childNode!.getId() === "N3") { + console.log(TEST_TAG + " getChild(3, ExpandMode.LAZY_EXPAND) result: success."); + } else { + console.log(TEST_TAG + " getChild(3, ExpandMode.LAZY_EXPAND) result: fail."); + } + } + } + + @Entry + @Component + struct Index { + private myNodeController: MyNodeController = new MyNodeController(); + private scroller: Scroller = new Scroller(); + + build() { + Scroll(this.scroller) { + Column({ space: 8 }) { + Column() { + Text("This is a NodeContainer.") + .textAlign(TextAlign.Center) + .borderRadius(10) + .backgroundColor(0xFFFFFF) + .width('100%') + .fontSize(16) + NodeContainer(this.myNodeController) + .borderWidth(1) + .width(300) + .height(100) + } + + Button("getFirstChildIndexWithoutExpand") + .width(300) + .onClick(() => { + this.myNodeController.getFirstChildIndexWithoutExpand(); + }) + Button("getLastChildIndexWithoutExpand") + .width(300) + .onClick(() => { + this.myNodeController.getLastChildIndexWithoutExpand(); + }) + Button("getChildWithNotExpand") + .width(300) + .onClick(() => { + this.myNodeController.getChildWithNotExpand(); + }) + Button("getChildWithExpand") + .width(300) + .onClick(() => { + this.myNodeController.getChildWithExpand(); + }) + Button("getChildWithLazyExpand") + .width(300) + .onClick(() => { + this.myNodeController.getChildWithLazyExpand(); + }) + } + .width("100%") + } + .scrollable(ScrollDirection.Vertical) // 滚动方向纵向 + } + } + ``` + +2. NDK侧通过[OH_ArkUI_NodeUtils_GetAttachedNodeHandleById](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeutils_getattachednodehandlebyid)接口获取ArkTS组件,并通过懒展开模式获取对应的子组件信息。 + ```c++ + ArkUI_NodeHandle childNode = nullptr; + OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("N3", &childNode); + + uint32_t index = 0; + OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand(childNode, &index); + uint32_t index1 = 0; + OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand(childNode, &index1); + ArkUI_NodeHandle child = nullptr; + auto result = OH_ArkUI_NodeUtils_GetChildWithExpandMode(childNode, 3, child, 0); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "firstChildIndex - lastChildIndex == %{public}d -- %{public}d, -- getResult= %{public}d", + index, index1, result); + ``` + +3. 查看日志打印的对应错误码返回是否正确,以此判断是否成功获取到对应子节点。