已开启
8分文档:r1_1to3 #163147
liuhuayi_openharmony创建于 18 天前
8分文档:r1_1to3 #163147
已开启
共 10 个文件变更+368-200
| @@ -181,7 +181,7 @@ export default class EntryAbility extends UIAbility { | |||
| 181 | 181 | ||
| 182 | onWindowStageCreate(windowStage: window.WindowStage): void { | 182 | onWindowStageCreate(windowStage: window.WindowStage): void { |
| 183 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); | 183 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); |
| 184 | - // 在loadContent前调用时,vp2px会根据屏幕默认像素密度返回计算结果。 | 184 | + // 在loadContent前调用,此时无UI实例,vp2px会根据屏幕默认像素密度返回计算结果。 |
| 185 | let pxValue = vp2px(20); | 185 | let pxValue = vp2px(20); |
| 186 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); | 186 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); |
| 187 | windowStage.loadContent('pages/Index', (err) => { | 187 | windowStage.loadContent('pages/Index', (err) => { |
| @@ -193,9 +193,7 @@ export default class EntryAbility extends UIAbility { | |||
| 193 | let pxValue = vp2px(20); | 193 | let pxValue = vp2px(20); |
| 194 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); | 194 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); |
| 195 | }); | 195 | }); |
| 196 | - // loadContent是异步接口,在此处调用不能保证UI实例已经创建成功。 | 196 | + // loadContent是异步接口,在loadContent之后并不能保证UI实例已经创建并初始化。此时调用例如vp2px等依赖UI实例的函数,可能因实例不正确导致结果不符合预期。 |
| 197 | - pxValue = vp2px(20); | ||
| 198 | - hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); | ||
| 199 | } | 197 | } |
| 200 | 198 | ||
| 201 | // ... | 199 | // ... |
| @@ -228,14 +226,13 @@ export default class EntryAbility extends UIAbility { | |||
| 228 | // 注册主窗的回调。 | 226 | // 注册主窗的回调。 |
| 229 | WindowUIContextUtils.registerWindowCallback(window); | 227 | WindowUIContextUtils.registerWindowCallback(window); |
| 230 | // 在loadContent前调用getUIContext时,UI实例未创建,存在异常。 | 228 | // 在loadContent前调用getUIContext时,UI实例未创建,存在异常。 |
| 229 | + // 调用loadContent后,UI实例的创建与初始化为异步进行。因此,UIContext的获取应在loadContent的完成回调中,而不是在调用loadContent之后。 | ||
| 231 | windowStage.loadContent('pages/Index', localStorage, (err) => { | 230 | windowStage.loadContent('pages/Index', localStorage, (err) => { |
| 232 | - // 需要在loadContent完成后获取UIContext。 | ||
| 233 | if (err.code) { | 231 | if (err.code) { |
| 234 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); | 232 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); |
| 235 | return; | 233 | return; |
| 236 | } | 234 | } |
| 237 | hilog.info(DOMAIN, 'testTag', `loadContent success.`); | 235 | hilog.info(DOMAIN, 'testTag', `loadContent success.`); |
| 238 | - // 需要在回调中调用。 | ||
| 239 | try { | 236 | try { |
| 240 | let uiContext = window.getUIContext(); | 237 | let uiContext = window.getUIContext(); |
| 241 | PixelUtils.setUIContext(uiContext); | 238 | PixelUtils.setUIContext(uiContext); |
| @@ -250,7 +247,7 @@ export default class EntryAbility extends UIAbility { | |||
| 250 | } catch (e) { | 247 | } catch (e) { |
| 251 | hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${e}`); | 248 | hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${e}`); |
| 252 | } | 249 | } |
| 253 | - // loadContent是异步接口,在此处调用不能保证UI实例已经创建成功。 | 250 | + // loadContent是异步接口,在loadContent之后并不能保证UI实例已经创建并初始化。此时调用例如vp2px等依赖UI实例的函数,可能因实例不正确导致结果不符合预期。 |
| 254 | }); | 251 | }); |
| 255 | } | 252 | } |
| 256 | 253 | ||
| @@ -259,7 +256,7 @@ export default class EntryAbility extends UIAbility { | |||
| 259 | onWindowStageDestroy(): void { | 256 | onWindowStageDestroy(): void { |
| 260 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); | 257 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); |
| 261 | // 在窗口销毁时需要移除失效的UIContext | 258 | // 在窗口销毁时需要移除失效的UIContext |
| 262 | - PixelUtil.removeUIContext(); | 259 | + PixelUtils.removeUIContext(); |
| 263 | } | 260 | } |
| 264 | 261 | ||
| 265 | // ... | 262 | // ... |
| @@ -303,9 +300,7 @@ export default class EntryAbility extends UIAbility { | |||
| 303 | let pxValue = vp2px(20); | 300 | let pxValue = vp2px(20); |
| 304 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); | 301 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); |
| 305 | }); | 302 | }); |
| 306 | - // loadContent是异步接口,在此处调用不能保证UI实例已经创建成功。 | 303 | + // loadContent是异步接口,在loadContent之后并不能保证UI实例已经创建并初始化。此时调用例如vp2px等依赖UI实例的函数,可能因实例不正确导致结果不符合预期。 |
| 307 | - pxValue = vp2px(20); | ||
| 308 | - hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); | ||
| 309 | } | 304 | } |
| 310 | 305 | ||
| 311 | // ... | 306 | // ... |
| @@ -386,7 +381,7 @@ export default class EntryAbility extends UIAbility { | |||
| 386 | let pxValue = resolvedUIContext.vp2px(20); | 381 | let pxValue = resolvedUIContext.vp2px(20); |
| 387 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); | 382 | hilog.info(DOMAIN, 'testTag', `20vp equals to ${pxValue}px`); |
| 388 | }); | 383 | }); |
| 389 | - // loadContent是异步接口,在此处调用不能保证UI实例已经创建成功。 | 384 | + // loadContent是异步接口,在loadContent之后并不能保证UI实例已经创建并初始化。此时调用例如vp2px等依赖UI实例的函数,可能因实例不正确导致结果不符合预期。 |
| 390 | } | 385 | } |
| 391 | 386 | ||
| 392 | onWindowStageDestroy(): void { | 387 | onWindowStageDestroy(): void { |
| @@ -582,14 +577,13 @@ export default class EntryAbility extends UIAbility { | |||
| 582 | // 注册主窗的回调。 | 577 | // 注册主窗的回调。 |
| 583 | WindowUIContextUtils.registerWindowCallback(window); | 578 | WindowUIContextUtils.registerWindowCallback(window); |
| 584 | // 在loadContent前调用getUIContext时,UI实例未创建,存在异常。 | 579 | // 在loadContent前调用getUIContext时,UI实例未创建,存在异常。 |
| 580 | + // 调用loadContent后,UI实例的创建与初始化为异步进行。因此,UIContext的获取应在loadContent的完成回调中,而不是在调用loadContent之后。 | ||
| 585 | windowStage.loadContent('pages/Index', localStorage, (err) => { | 581 | windowStage.loadContent('pages/Index', localStorage, (err) => { |
| 586 | - // 需要在loadContent完成后获取UIContext。 | ||
| 587 | if (err.code) { | 582 | if (err.code) { |
| 588 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); | 583 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); |
| 589 | return; | 584 | return; |
| 590 | } | 585 | } |
| 591 | hilog.info(DOMAIN, 'testTag', `loadContent success.`); | 586 | hilog.info(DOMAIN, 'testTag', `loadContent success.`); |
| 592 | - // 需要在回调中调用。 | ||
| 593 | try { | 587 | try { |
| 594 | let uiContext = window.getUIContext(); | 588 | let uiContext = window.getUIContext(); |
| 595 | PixelUtils.setUIContext(uiContext); | 589 | PixelUtils.setUIContext(uiContext); |
| @@ -604,8 +598,8 @@ export default class EntryAbility extends UIAbility { | |||
| 604 | } catch (e) { | 598 | } catch (e) { |
| 605 | hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${e}`); | 599 | hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${e}`); |
| 606 | } | 600 | } |
| 607 | - // loadContent是异步接口,在此处调用不能保证UI实例已经创建成功。 | 601 | + // loadContent是异步接口,在loadContent之后并不能保证UI实例已经创建并初始化。此时调用例如vp2px等依赖UI实例的函数,可能因实例不正确导致结果不符合预期。 |
| 608 | - }); | 602 | + }); |
| 609 | } | 603 | } |
| 610 | 604 | ||
| 611 | // ... | 605 | // ... |
| @@ -613,7 +607,7 @@ export default class EntryAbility extends UIAbility { | |||
| 613 | onWindowStageDestroy(): void { | 607 | onWindowStageDestroy(): void { |
| 614 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); | 608 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); |
| 615 | // 在窗口销毁时需要移除失效的UIContext | 609 | // 在窗口销毁时需要移除失效的UIContext |
| 616 | - PixelUtil.removeUIContext(); | 610 | + PixelUtils.removeUIContext(); |
| 617 | } | 611 | } |
| 618 | 612 | ||
| 619 | // ... | 613 | // ... |
| @@ -699,7 +693,7 @@ export class WindowUIContextUtils { | |||
| 699 | } | 693 | } |
| 700 | }); | 694 | }); |
| 701 | } catch (exception) { | 695 | } catch (exception) { |
| 702 | - console.error(`Failed to unregister callback. Cause: ${exception}`); | 696 | + console.error(`Failed to register callback. Cause: ${exception}`); |
| 703 | } | 697 | } |
| 704 | } | 698 | } |
| 705 | 699 | ||
| @@ -748,14 +742,13 @@ export default class EntryAbility extends UIAbility { | |||
| 748 | // 注册主窗的回调。 | 742 | // 注册主窗的回调。 |
| 749 | WindowUIContextUtils.registerWindowCallback(window); | 743 | WindowUIContextUtils.registerWindowCallback(window); |
| 750 | // 在loadContent前调用getUIContext时,UI实例未创建,存在异常。 | 744 | // 在loadContent前调用getUIContext时,UI实例未创建,存在异常。 |
| 745 | + // 调用loadContent后,UI实例的创建与初始化为异步进行。因此,UIContext的获取应在loadContent的完成回调中,而不是在调用loadContent之后。 | ||
| 751 | windowStage.loadContent('pages/Index', localStorage, (err) => { | 746 | windowStage.loadContent('pages/Index', localStorage, (err) => { |
| 752 | - // 需要在loadContent完成后获取UIContext。 | ||
| 753 | if (err.code) { | 747 | if (err.code) { |
| 754 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); | 748 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); |
| 755 | return; | 749 | return; |
| 756 | } | 750 | } |
| 757 | hilog.info(DOMAIN, 'testTag', `loadContent success.`); | 751 | hilog.info(DOMAIN, 'testTag', `loadContent success.`); |
| 758 | - // 需要在回调中调用。 | ||
| 759 | try { | 752 | try { |
| 760 | let uiContext = window.getUIContext(); | 753 | let uiContext = window.getUIContext(); |
| 761 | PixelUtils.setUIContext(uiContext); | 754 | PixelUtils.setUIContext(uiContext); |
| @@ -770,7 +763,7 @@ export default class EntryAbility extends UIAbility { | |||
| 770 | } catch (e) { | 763 | } catch (e) { |
| 771 | hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${e}`); | 764 | hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${e}`); |
| 772 | } | 765 | } |
| 773 | - // loadContent是异步接口,在此处调用不能保证UI实例已经创建成功。 | 766 | + // loadContent是异步接口,在loadContent之后并不能保证UI实例已经创建并初始化。此时调用例如vp2px等依赖UI实例的函数,可能因实例不正确导致结果不符合预期。 |
| 774 | }); | 767 | }); |
| 775 | } | 768 | } |
| 776 | 769 | ||
| @@ -944,6 +937,10 @@ export class PixelUtils { | |||
| 944 | PixelUtils.uiContext = uiContext; | 937 | PixelUtils.uiContext = uiContext; |
| 945 | } | 938 | } |
| 946 | 939 | ||
| 940 | + static removeUIContext(): void { | ||
| 941 | + PixelUtils.uiContext = undefined; | ||
| 942 | + } | ||
| 943 | + | ||
| 947 | static vp2px(vpValue: number, uiContext?: UIContext): number | undefined { | 944 | static vp2px(vpValue: number, uiContext?: UIContext): number | undefined { |
| 948 | let _uiContext = uiContext ?? PixelUtils.uiContext; | 945 | let _uiContext = uiContext ?? PixelUtils.uiContext; |
| 949 | if (!_uiContext || !_uiContext.isAvailable()) { | 946 | if (!_uiContext || !_uiContext.isAvailable()) { |
| @@ -1032,6 +1029,9 @@ struct GetContextPage { | |||
| 1032 | 1029 | ||
| 1033 | ``` TypeScript | 1030 | ``` TypeScript |
| 1034 | // Common/ContextUtils.ets | 1031 | // Common/ContextUtils.ets |
| 1032 | +import { Context } from '@kit.AbilityKit'; | ||
| 1033 | +import { UIContext } from '@kit.ArkUI'; | ||
| 1034 | + | ||
| 1035 | export class ContextUtils { | 1035 | export class ContextUtils { |
| 1036 | public static context: Context | undefined; | 1036 | public static context: Context | undefined; |
| 1037 | 1037 | ||
| @@ -1075,6 +1075,7 @@ export default class EntryAbility extends UIAbility { | |||
| 1075 | 1075 | ||
| 1076 | onDestroy(): void { | 1076 | onDestroy(): void { |
| 1077 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); | 1077 | hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); |
| 1078 | + ContextUtils.context = undefined; | ||
| 1078 | } | 1079 | } |
| 1079 | // ... | 1080 | // ... |
| 1080 | } | 1081 | } |
| @@ -1180,7 +1181,6 @@ struct LocalStoragePage { | |||
| 1180 | let storage = uiContext.getSharedLocalStorage(); | 1181 | let storage = uiContext.getSharedLocalStorage(); |
| 1181 | if (storage) { | 1182 | if (storage) { |
| 1182 | storage.setOrCreate('message', 'onClick is called.'); | 1183 | storage.setOrCreate('message', 'onClick is called.'); |
| 1183 | - this.message = 'LocalStoragePageHelloWorld'; | ||
| 1184 | } | 1184 | } |
| 1185 | }) | 1185 | }) |
| 1186 | } | 1186 | } |
| @@ -1213,8 +1213,8 @@ export default class EntryAbility extends UIAbility { | |||
| 1213 | let localStorage = new LocalStorage(); | 1213 | let localStorage = new LocalStorage(); |
| 1214 | localStorage.setOrCreate('message', 'Message from Storage') | 1214 | localStorage.setOrCreate('message', 'Message from Storage') |
| 1215 | // ... | 1215 | // ... |
| 1216 | + // 调用loadContent后,UI实例的创建与初始化为异步进行。因此,UIContext的获取应在loadContent的完成回调中,而不是在调用loadContent之后。 | ||
| 1216 | windowStage.loadContent('pages/Index', localStorage, (err) => { | 1217 | windowStage.loadContent('pages/Index', localStorage, (err) => { |
| 1217 | - // 需要在loadContent完成后获取UIContext。 | ||
| 1218 | if (err.code) { | 1218 | if (err.code) { |
| 1219 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); | 1219 | hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); |
| 1220 | return; | 1220 | return; |
| @@ -20,7 +20,7 @@ ArkUI支持在前端使用ArkTS语言创建命令式节点,即[FrameNode](../r | |||
| 20 | 20 | ||
| 21 | 跨语言指的是跨越ArkTS语言和C语言。跨语言配置指的是命令式节点上对于跨语言操作的权限配置。 | 21 | 跨语言指的是跨越ArkTS语言和C语言。跨语言配置指的是命令式节点上对于跨语言操作的权限配置。 |
| 22 | 22 | ||
| 23 | -可以通过[setCrossLanguageOptions](../reference/apis-arkui/js-apis-arkui-frameNode.md#setcrosslanguageoptions15)与[OH_ArkUI_NodeUtils_SetCrossLanguageOption](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_setcrosslanguageoption)接口设置当前节点的跨语言配置。如果当前节点无法修改或设置跨语言配置,则会抛出异常信息。 | 23 | +可以通过[setCrossLanguageOptions](../reference/apis-arkui/js-apis-arkui-frameNode.md#setcrosslanguageoptions15)与[OH_ArkUI_NodeUtils_SetCrossLanguageOption](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_setcrosslanguageoption)接口设置当前节点的跨语言配置。调用setCrossLanguageOptions时,如果当前节点无法修改或设置跨语言配置,会抛出异常;调用OH_ArkUI_NodeUtils_SetCrossLanguageOption时,可通过接口返回的错误码判断是否设置成功。 |
| 24 | 24 | ||
| 25 | 可以使用[getCrossLanguageOptions](../reference/apis-arkui/js-apis-arkui-frameNode.md#getcrosslanguageoptions15)与[OH_ArkUI_NodeUtils_GetCrossLanguageOption](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getcrosslanguageoption)接口获取当前节点的跨语言配置。 | 25 | 可以使用[getCrossLanguageOptions](../reference/apis-arkui/js-apis-arkui-frameNode.md#getcrosslanguageoptions15)与[OH_ArkUI_NodeUtils_GetCrossLanguageOption](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getcrosslanguageoption)接口获取当前节点的跨语言配置。 |
| 26 | 26 | ||
| @@ -168,6 +168,10 @@ struct CrossLanguage { | |||
| 168 | nativeNode.createNativeRoot(this.rootSlot); | 168 | nativeNode.createNativeRoot(this.rootSlot); |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | + aboutToDisappear(): void { | ||
| 172 | + nativeNode.destroyNativeRoot(); | ||
| 173 | + } | ||
| 174 | + | ||
| 171 | build() { | 175 | build() { |
| 172 | Scroll() { | 176 | Scroll() { |
| 173 | Column({ space: 15 }) { | 177 | Column({ space: 15 }) { |
| @@ -207,7 +211,7 @@ struct CrossLanguage { | |||
| 207 | ``` | 211 | ``` |
| 208 | 212 | ||
| 209 | 2. 新建`CrossLanguageExample.h`文件,在其中获取到目标节点(该节点在ArkTS侧创建),并设置属性。 | 213 | 2. 新建`CrossLanguageExample.h`文件,在其中获取到目标节点(该节点在ArkTS侧创建),并设置属性。 |
| 210 | - ```c | 214 | + ```cpp |
| 211 | // CrossLanguageExample.h | 215 | // CrossLanguageExample.h |
| 212 | #ifndef MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H | 216 | #ifndef MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H |
| 213 | #define MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H | 217 | #define MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H |
| @@ -250,7 +254,7 @@ struct CrossLanguage { | |||
| 250 | ArkUI_NumberValue text_height[] = {50}; | 254 | ArkUI_NumberValue text_height[] = {50}; |
| 251 | ArkUI_AttributeItem text_item1 = {text_height, sizeof(text_height) / sizeof(ArkUI_NumberValue)}; | 255 | ArkUI_AttributeItem text_item1 = {text_height, sizeof(text_height) / sizeof(ArkUI_NumberValue)}; |
| 252 | nodeAPI->setAttribute(text0, NODE_HEIGHT, &text_item1); | 256 | nodeAPI->setAttribute(text0, NODE_HEIGHT, &text_item1); |
| 253 | - ArkUI_AttributeItem text_item = {.string = "C设置TS创建的节点属性"}; | 257 | + ArkUI_AttributeItem text_item = {.string = "C设置ArkTS创建的节点属性"}; |
| 254 | nodeAPI->setAttribute(text0, NODE_TEXT_CONTENT, &text_item); | 258 | nodeAPI->setAttribute(text0, NODE_TEXT_CONTENT, &text_item); |
| 255 | ArkUI_NumberValue margin[] = {10}; | 259 | ArkUI_NumberValue margin[] = {10}; |
| 256 | ArkUI_AttributeItem item_margin = {margin, sizeof(margin) / sizeof(ArkUI_NumberValue)}; | 260 | ArkUI_AttributeItem item_margin = {margin, sizeof(margin) / sizeof(ArkUI_NumberValue)}; |
| @@ -285,30 +289,39 @@ struct CrossLanguage { | |||
| 285 | 289 | ||
| 286 | // 注册事件 | 290 | // 注册事件 |
| 287 | auto onClick = [](ArkUI_NodeEvent *event) { | 291 | auto onClick = [](ArkUI_NodeEvent *event) { |
| 288 | - ArkUI_NodeHandle node = OH_ArkUI_NodeEvent_GetNodeHandle(event); | ||
| 289 | auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); | 292 | auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); |
| 290 | 293 | ||
| 291 | if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) { // scrollBarColor | 294 | if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) { // scrollBarColor |
| 292 | ArkUI_NodeHandle node_ptr = nullptr; | 295 | ArkUI_NodeHandle node_ptr = nullptr; |
| 293 | - OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("scroll", &node_ptr); | 296 | + if (OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("scroll", &node_ptr) != |
| 294 | - try { | 297 | + ARKUI_ERROR_CODE_NO_ERROR || node_ptr == nullptr) { |
| 295 | - ArkUI_NumberValue scroll_color_value[] = {{.u32 = 0xff00ff00}}; | 298 | + OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", "failed to get scroll node"); |
| 296 | - ArkUI_AttributeItem scroll_color_item = {scroll_color_value, sizeof(scroll_color_value) / sizeof(ArkUI_NumberValue)}; | 299 | + return; |
| 297 | - nodeAPI->setAttribute(node_ptr, NODE_SCROLL_BAR_COLOR, &scroll_color_item); | 300 | + } |
| 298 | - } catch (...) { | 301 | + ArkUI_NumberValue scroll_color_value[] = {{.u32 = 0xff00ff00}}; |
| 299 | - OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", "crossLanguage setAttribute error"); | 302 | + ArkUI_AttributeItem scroll_color_item = { |
| 303 | + scroll_color_value, sizeof(scroll_color_value) / sizeof(ArkUI_NumberValue)}; | ||
| 304 | + int32_t result = nodeAPI->setAttribute(node_ptr, NODE_SCROLL_BAR_COLOR, &scroll_color_item); | ||
| 305 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 306 | + OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", | ||
| 307 | + "set scroll bar color failed, error code: %{public}d", result); | ||
| 300 | } | 308 | } |
| 301 | } | 309 | } |
| 302 | 310 | ||
| 303 | if (OH_ArkUI_NodeEvent_GetTargetId(event) == 1) { // scrollBarWidth | 311 | if (OH_ArkUI_NodeEvent_GetTargetId(event) == 1) { // scrollBarWidth |
| 304 | ArkUI_NodeHandle node_ptr = nullptr; | 312 | ArkUI_NodeHandle node_ptr = nullptr; |
| 305 | - OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("scroll", &node_ptr); | 313 | + if (OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("scroll", &node_ptr) != |
| 306 | - try { | 314 | + ARKUI_ERROR_CODE_NO_ERROR || node_ptr == nullptr) { |
| 307 | - ArkUI_NumberValue scroll_width_value[] = {{20}}; | 315 | + OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", "failed to get scroll node"); |
| 308 | - ArkUI_AttributeItem scroll_width_item = {scroll_width_value, sizeof(scroll_width_value) / sizeof(ArkUI_NumberValue)}; | 316 | + return; |
| 309 | - nodeAPI->setAttribute(node_ptr, NODE_SCROLL_BAR_WIDTH, &scroll_width_item); | 317 | + } |
| 310 | - } catch (...) { | 318 | + ArkUI_NumberValue scroll_width_value[] = {{20}}; |
| 311 | - OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", "crossLanguage setAttribute error"); | 319 | + ArkUI_AttributeItem scroll_width_item = { |
| 320 | + scroll_width_value, sizeof(scroll_width_value) / sizeof(ArkUI_NumberValue)}; | ||
| 321 | + int32_t result = nodeAPI->setAttribute(node_ptr, NODE_SCROLL_BAR_WIDTH, &scroll_width_item); | ||
| 322 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 323 | + OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", | ||
| 324 | + "set scroll bar width failed, error code: %{public}d", result); | ||
| 312 | } | 325 | } |
| 313 | } | 326 | } |
| 314 | }; | 327 | }; |
| @@ -329,7 +342,7 @@ struct CrossLanguage { | |||
| 329 | ``` | 342 | ``` |
| 330 | 343 | ||
| 331 | 3. 在`NativeEntry.cpp`中,挂载Native节点。 | 344 | 3. 在`NativeEntry.cpp`中,挂载Native节点。 |
| 332 | - ```c | 345 | + ```cpp |
| 333 | // NativeEntry.cpp | 346 | // NativeEntry.cpp |
| 334 | 347 | ||
| 335 | 348 | ||
| @@ -377,8 +390,8 @@ struct CrossLanguage { | |||
| 377 | ``` | 390 | ``` |
| 378 | 391 | ||
| 379 | 4. 修改CMakeLists.txt,添加链接库。 | 392 | 4. 修改CMakeLists.txt,添加链接库。 |
| 380 | - ```c | 393 | + ```cmake |
| 381 | - // CMakeLists.txt | 394 | + # CMakeLists.txt |
| 382 | # the minimum version of CMake. | 395 | # the minimum version of CMake. |
| 383 | cmake_minimum_required(VERSION 3.5.0) | 396 | cmake_minimum_required(VERSION 3.5.0) |
| 384 | project(CAPI_DEMO) | 397 | project(CAPI_DEMO) |
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | 实例状态更新的日志格式为: | 21 | 实例状态更新的日志格式为: |
| 22 | 22 | ||
| 23 | - `({currentId}:{trackedId}:{trackedReason})][{bundleName}][{moduleName}][{thisInstanceId}]: window {status}` | 23 | + `({currentId}:{trackedId}:{trackedReason})] [{bundleName}][{moduleName}][{thisInstanceId}]: window {status}` |
| 24 | 24 | ||
| 25 | 各字段含义为: | 25 | 各字段含义为: |
| 26 | 26 | ||
| @@ -33,8 +33,8 @@ | |||
| 33 | |{trackedReason}| 字符串 | singleton | 间接跟踪的原因,开发者可不关注该字段。 | | 33 | |{trackedReason}| 字符串 | singleton | 间接跟踪的原因,开发者可不关注该字段。 | |
| 34 | |{bundleName}| 字符串 | com.example.helloworld| 应用的bundleName。 | | 34 | |{bundleName}| 字符串 | com.example.helloworld| 应用的bundleName。 | |
| 35 | |{moduleName}| 字符串 | entry| 当前模块的moduleName。 | | 35 | |{moduleName}| 字符串 | entry| 当前模块的moduleName。 | |
| 36 | - |{thisInstanceId}| 正数 | 100000 | 被通知UI实例的ID。 | | 36 | + |{thisInstanceId}| 正整数 | 100000 | 被通知UI实例的ID。 | |
| 37 | - |{status}| 实例被通知的状态 | focus | 可选值为:<br> - focus:获焦<br> - unfocus:失焦<br> - foreground:前台<br> - background:后台<br> - destroy:销毁 | | 37 | + |{status}| 字符串 | focus | 实例被通知的状态。可选值为:<br> - focus:获焦<br> - unfocus:失焦<br> - foreground:前台<br> - background:后台<br> - destroy:销毁 | |
| 38 | 38 | ||
| 39 | 可使用如下正则表达式匹配相关日志: | 39 | 可使用如下正则表达式匹配相关日志: |
| 40 | 40 | ||
| @@ -48,9 +48,9 @@ | |||
| 48 | 48 | ||
| 49 | - 异常日志示例: | 49 | - 异常日志示例: |
| 50 | 50 | ||
| 51 | - `(100000:100000:scoped)] [com.example.helloworld][entry][100001]: window background` | 51 | + `(100000:100000:scope)] [com.example.helloworld][entry][100001]: window background` |
| 52 | 52 | ||
| 53 | - 该异常日志说明存在错误跟踪ID为100000的UI实例。 | 53 | + 该异常日志说明当前上下文实例ID异常,其值为100000。 |
| 54 | 54 | ||
| 55 | - 若前文出现以下特定格式的日志: | 55 | - 若前文出现以下特定格式的日志: |
| 56 | 56 | ||
| @@ -146,12 +146,12 @@ OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativ | |||
| 146 | |——cpp | 146 | |——cpp |
| 147 | | |——types | 147 | | |——types |
| 148 | | | |——libentry | 148 | | | |——libentry |
| 149 | -| | | |——index.d.ts 提供Native和ArkTS侧的桥接方法。 | 149 | +| | | |——Index.d.ts 提供Native和ArkTS侧的桥接方法。 |
| 150 | -| |——napi_init.cpp 与index.d.ts对应的桥接方法对接Native侧的定义处。 | 150 | +| |——napi_init.cpp 注册Node-API模块,并将Index.d.ts中声明的接口与Native侧实现函数绑定。 |
| 151 | | |——NativeEntry.cpp 桥接方法的Native侧实现。 | 151 | | |——NativeEntry.cpp 桥接方法的Native侧实现。 |
| 152 | | |——NativeEntry.h 桥接方法的Native侧定义。 | 152 | | |——NativeEntry.h 桥接方法的Native侧定义。 |
| 153 | | |——NativeModule.h 提供获取ArkUI在Native侧模块的封装接口。 | 153 | | |——NativeModule.h 提供获取ArkUI在Native侧模块的封装接口。 |
| 154 | -| |——CMakeLists.txt C语言库引用文件。 | 154 | +| |——CMakeLists.txt CMake构建配置文件,用于配置编译源文件和链接库。 |
| 155 | | |——ArkUIBaseNode.h 节点封装扩展类。 | 155 | | |——ArkUIBaseNode.h 节点封装扩展类。 |
| 156 | | |——ArkUINode.h 节点封装扩展类。 | 156 | | |——ArkUINode.h 节点封装扩展类。 |
| 157 | | |——ArkUIListNode.h 节点封装扩展类。 | 157 | | |——ArkUIListNode.h 节点封装扩展类。 |
| @@ -161,7 +161,7 @@ OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativ | |||
| 161 | | | 161 | | |
| 162 | |——ets | 162 | |——ets |
| 163 | | |——pages | 163 | | |——pages |
| 164 | -| |——entry.ets 应用启动页,加载承载Native的容器。 | 164 | +| |——Index.ets 应用启动页,加载承载Native组件的容器。 |
| 165 | | | 165 | | |
| 166 | ``` | 166 | ``` |
| 167 | 167 | ||
| @@ -262,7 +262,7 @@ OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativ | |||
| 262 | extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } | 262 | extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } |
| 263 | ``` | 263 | ``` |
| 264 | 264 | ||
| 265 | -3. 在NativeEntry.h文件中创建Native界面。 | 265 | +3. 在NativeEntry.h中声明桥接函数和Native组件生命周期管理类,并在NativeEntry.cpp中实现组件的创建、挂载与销毁逻辑。 |
| 266 | 266 | ||
| 267 | <!-- @[Cpp_Native](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/ButtonList/entry/src/main/cpp/NativeEntry.h) --> | 267 | <!-- @[Cpp_Native](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/ButtonList/entry/src/main/cpp/NativeEntry.h) --> |
| 268 | 268 | ||
| @@ -363,7 +363,7 @@ OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativ | |||
| 363 | ``` | 363 | ``` |
| 364 | 364 | ||
| 365 | 365 | ||
| 366 | - 使用NDK提供的C接口需要在CMakeLists.txt中增加libace_ndk.z.so的引用,如下所示。其中entry为工程导出的动态库名称,如当前示例使用的是默认的名称libentry.so。新增cpp文件后,同样需要在CMakeLists.txt中添加相应的cpp文件。若未进行此配置,对应的文件将不会被编译。 | 366 | + 使用NDK提供的C接口需要在CMakeLists.txt中增加libace_ndk.z.so的引用,如下所示。其中entry为CMake目标名称,默认生成的动态库文件名为libentry.so。新增cpp文件后,同样需要在CMakeLists.txt中添加相应的cpp文件。若未进行此配置,对应的文件将不会被编译。 |
| 367 | 367 | ||
| 368 | ``` c | 368 | ``` c |
| 369 | add_library(entry SHARED napi_init.cpp NativeEntry.cpp) | 369 | add_library(entry SHARED napi_init.cpp NativeEntry.cpp) |
| @@ -11,7 +11,7 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 11 | 11 | ||
| 12 | > **说明:** | 12 | > **说明:** |
| 13 | > | 13 | > |
| 14 | -> - 自定义组件事件注册需要[addNodeCustomEventReceiver](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#addnodecustomeventreceiver)声明监听器注册和[registerNodeCustomEvent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#registernodecustomevent)声明需要的自定义事件类型,监听器只能监听已声明的事件。 | 14 | +> - 使用自定义组件事件时,需要通过[addNodeCustomEventReceiver](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#addnodecustomeventreceiver)添加事件监听器,并通过[registerNodeCustomEvent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#registernodecustomevent)声明需要监听的自定义事件类型,监听器只能监听已声明的事件。 |
| 15 | > | 15 | > |
| 16 | > - 需要关注事件的反注册逻辑,如在组件销毁前调用[removeNodeCustomEventReceiver](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#removenodecustomeventreceiver)移除事件监听器,[unregisterNodeCustomEvent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#unregisternodecustomevent)通知ArkUI框架已监听的自定义组件事件不再需要监听。 | 16 | > - 需要关注事件的反注册逻辑,如在组件销毁前调用[removeNodeCustomEventReceiver](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#removenodecustomeventreceiver)移除事件监听器,[unregisterNodeCustomEvent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#unregisternodecustomevent)通知ArkUI框架已监听的自定义组件事件不再需要监听。 |
| 17 | > | 17 | > |
| @@ -71,7 +71,7 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 71 | void SetPadding(int32_t padding) | 71 | void SetPadding(int32_t padding) |
| 72 | { | 72 | { |
| 73 | padding_ = padding; | 73 | padding_ = padding; |
| 74 | - // 自定义属性事件更新需要主动调用标记脏区接口。 | 74 | + // 自定义布局属性更新后,需要主动标记测算脏区。 |
| 75 | nativeModule_->markDirty(handle_, NODE_NEED_MEASURE); | 75 | nativeModule_->markDirty(handle_, NODE_NEED_MEASURE); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| @@ -121,7 +121,7 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 121 | maxHeight = size.height; | 121 | maxHeight = size.height; |
| 122 | } | 122 | } |
| 123 | } | 123 | } |
| 124 | - // 自定义测算为所有子节点大小加固定边距。该自定义节点最终的尺寸以此处设置的值为准。 | 124 | + // 自定义测算结果为所有子节点中的最大宽度和最大高度分别加上两侧固定边距。该自定义节点最终的尺寸以此处设置的值为准。 |
| 125 | const int paddingMultiplier = 2; | 125 | const int paddingMultiplier = 2; |
| 126 | nativeModule_->setMeasuredSize(handle_, maxWidth + paddingMultiplier * padding_, | 126 | nativeModule_->setMeasuredSize(handle_, maxWidth + paddingMultiplier * padding_, |
| 127 | maxHeight + paddingMultiplier * padding_); | 127 | maxHeight + paddingMultiplier * padding_); |
| @@ -537,7 +537,6 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 537 | 537 | ||
| 538 | // 网格单元配置 | 538 | // 网格单元配置 |
| 539 | struct GridItemConfig { | 539 | struct GridItemConfig { |
| 540 | - int32_t rowSpan = 1; // 占据的行数 | ||
| 541 | int32_t columnSpan = 1; // 占据的列数 | 540 | int32_t columnSpan = 1; // 占据的列数 |
| 542 | }; | 541 | }; |
| 543 | 542 | ||
| @@ -578,10 +577,9 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 578 | } | 577 | } |
| 579 | 578 | ||
| 580 | // 设置子组件的网格配置 | 579 | // 设置子组件的网格配置 |
| 581 | - void SetItemConfig(ArkUI_NodeHandle child, int32_t rowSpan, int32_t columnSpan) | 580 | + void SetItemConfig(ArkUI_NodeHandle child, int32_t columnSpan) |
| 582 | { | 581 | { |
| 583 | GridItemConfig config; | 582 | GridItemConfig config; |
| 584 | - config.rowSpan = rowSpan; | ||
| 585 | config.columnSpan = columnSpan; | 583 | config.columnSpan = columnSpan; |
| 586 | itemConfigs_[child] = config; | 584 | itemConfigs_[child] = config; |
| 587 | nativeModule_->markDirty(handle_, NODE_NEED_MEASURE); | 585 | nativeModule_->markDirty(handle_, NODE_NEED_MEASURE); |
| @@ -609,7 +607,7 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 609 | void MeasureChild(ArkUI_NodeHandle child, int32_t cellWidth, | 607 | void MeasureChild(ArkUI_NodeHandle child, int32_t cellWidth, |
| 610 | ArkUI_LayoutConstraint *childConstraint, std::vector<int32_t> &columnHeights) | 608 | ArkUI_LayoutConstraint *childConstraint, std::vector<int32_t> &columnHeights) |
| 611 | { | 609 | { |
| 612 | - GridItemConfig config = {1, 1}; | 610 | + GridItemConfig config = {1}; |
| 613 | auto it = itemConfigs_.find(child); | 611 | auto it = itemConfigs_.find(child); |
| 614 | if (it != itemConfigs_.end()) { | 612 | if (it != itemConfigs_.end()) { |
| 615 | config = it->second; | 613 | config = it->second; |
| @@ -838,13 +836,13 @@ ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力 | |||
| 838 | const std::vector<uint32_t>& colors) | 836 | const std::vector<uint32_t>& colors) |
| 839 | { | 837 | { |
| 840 | for (size_t i = 0; i < itemSizes.size(); ++i) { | 838 | for (size_t i = 0; i < itemSizes.size(); ++i) { |
| 841 | - auto itemNode = nodeAPI->createNode(ARKUI_NODE_STACK); | 839 | + auto itemNode = std::make_shared<ArkUINode>(nodeAPI->createNode(ARKUI_NODE_STACK)); |
| 842 | - SetNodeColorAttribute(nodeAPI, itemNode, colors[i % colors.size()]); | 840 | + SetNodeColorAttribute(nodeAPI, itemNode->GetHandle(), colors[i % colors.size()]); |
| 843 | - SetNodeBorderRadiusAttribute(nodeAPI, itemNode, GRID_ITEM_RADIUS); | 841 | + SetNodeBorderRadiusAttribute(nodeAPI, itemNode->GetHandle(), GRID_ITEM_RADIUS); |
| 844 | - SetNodeBorderStyle(nodeAPI, itemNode); | 842 | + SetNodeBorderStyle(nodeAPI, itemNode->GetHandle()); |
| 845 | - SetNodeHeightByRowSpan(nodeAPI, itemNode, itemSizes[i].first); | 843 | + SetNodeHeightByRowSpan(nodeAPI, itemNode->GetHandle(), itemSizes[i].first); |
| 846 | - gridNode->SetItemConfig(itemNode, itemSizes[i].first, itemSizes[i].second); | 844 | + gridNode->SetItemConfig(itemNode->GetHandle(), itemSizes[i].second); |
| 847 | - nodeAPI->addChild(gridNode->GetHandle(), itemNode); | 845 | + gridNode->AddChild(itemNode); |
| 848 | } | 846 | } |
| 849 | } | 847 | } |
| 850 | } // namespace | 848 | } // namespace |
| @@ -88,7 +88,7 @@ | |||
| 88 | 88 | ||
| 89 | ## 多线程NDK接口的错误与异常 | 89 | ## 多线程NDK接口的错误与异常 |
| 90 | 90 | ||
| 91 | -多线程NDK接口调用规范请参考[多线程NDK接口集合规格](#多线程ndk接口集合规格)。调用多线程NDK接口时必须检查接口返回值,如下两种情况接口会返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | 91 | +多线程NDK接口调用规范请参考[多线程NDK接口集合规格](#多线程ndk接口集合规格)。在非UI线程中调用集合中不支持多线程的接口,或使用多线程NDK接口操作处于Attached状态的组件时,接口可能返回错误码(如[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode))、空指针、0或默认值,也可能调用不生效,具体行为请参考各接口的多线程规格。 |
| 92 | 92 | ||
| 93 | - 在非UI线程中调用集合中不支持多线程的接口。 | 93 | - 在非UI线程中调用集合中不支持多线程的接口。 |
| 94 | 94 | ||
| @@ -143,7 +143,7 @@ | |||
| 143 | | int32_t(\* [insertChildAfter](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#insertchildafter) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) child, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) sibling) | 将child节点挂载到parent节点的子节点列表中,挂载位置在sibling节点之后。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | | 143 | | int32_t(\* [insertChildAfter](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#insertchildafter) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) child, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) sibling) | 将child节点挂载到parent节点的子节点列表中,挂载位置在sibling节点之后。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | |
| 144 | | int32_t(\* [insertChildBefore](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#insertchildbefore) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) child, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) sibling) | 将child节点挂载到parent节点的子节点列表中,挂载位置在sibling节点之前。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | | 144 | | int32_t(\* [insertChildBefore](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#insertchildbefore) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) child, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) sibling) | 将child节点挂载到parent节点的子节点列表中,挂载位置在sibling节点之前。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | |
| 145 | | int32_t(\* [insertChildAt](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#insertchildat) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) child, int32_t position) | 将child节点挂载到parent节点的子节点列表中,挂载位置由position指定。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | | 145 | | int32_t(\* [insertChildAt](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#insertchildat) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent, [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) child, int32_t position) | 将child节点挂载到parent节点的子节点列表中,挂载位置由position指定。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | |
| 146 | -| [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md)(\* [getParent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#getparent) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) node) | 获取node节点的父节点。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | | 146 | +| [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md)(\* [getParent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#getparent) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) node) | 获取node节点的父节点。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回空指针。 | |
| 147 | | int32_t(\* [removeAllChildren](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#removeallchildren) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent) | 移除parent节点的所有子节点。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | | 147 | | int32_t(\* [removeAllChildren](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#removeallchildren) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) parent) | 移除parent节点的所有子节点。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回错误码[ARKUI_ERROR_CODE_NODE_ON_INVALID_THREAD](../reference/apis-arkui/capi-arkui-nativemodule-arkui-error-code-h.md#arkui_errorcode)。 | |
| 148 | | uint32_t(\* [getTotalChildCount](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#gettotalchildcount) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) node) | 获取node节点的子节点个数。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回0。 | | 148 | | uint32_t(\* [getTotalChildCount](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#gettotalchildcount) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) node) | 获取node节点的子节点个数。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回0。 | |
| 149 | | [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md)(\* [getChildAt](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#getchildat) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) node, int32_t position) | 获取node节点的子节点指针,位置由position指定。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回空指针。 | | 149 | | [ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md)(\* [getChildAt](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#getchildat) )([ArkUI_NodeHandle](../reference/apis-arkui/capi-arkui-nativemodule-arkui-node8h.md) node, int32_t position) | 获取node节点的子节点指针,位置由position指定。 | 支持 | 在非UI线程调用函数操作Attached节点时,接口返回空指针。 | |
| @@ -540,7 +540,6 @@ napi_value CreateNodeTreeOnMultiThread(napi_env env, napi_callback_info info) { | |||
| 540 | result = OH_ArkUI_GetContextFromNapiValue(env, args[1], &g_contextHandle); | 540 | result = OH_ArkUI_GetContextFromNapiValue(env, args[1], &g_contextHandle); |
| 541 | if (result != ARKUI_ERROR_CODE_NO_ERROR) { | 541 | if (result != ARKUI_ERROR_CODE_NO_ERROR) { |
| 542 | OH_LOG_ERROR(LOG_APP, "OH_ArkUI_GetContextFromNapiValue Failed %{public}d", result); | 542 | OH_LOG_ERROR(LOG_APP, "OH_ArkUI_GetContextFromNapiValue Failed %{public}d", result); |
| 543 | - delete g_contextHandle; | ||
| 544 | g_contextHandle = nullptr; | 543 | g_contextHandle = nullptr; |
| 545 | return nullptr; | 544 | return nullptr; |
| 546 | } | 545 | } |
| @@ -71,7 +71,7 @@ ArkUI NDK接口能力主要包括: | |||
| 71 | | [构建自定义组件](ndk-build-custom-components.md) | 介绍了如何使用NDK接口能力构建自定义组件,实现差异化UI组件。 | | 71 | | [构建自定义组件](ndk-build-custom-components.md) | 介绍了如何使用NDK接口能力构建自定义组件,实现差异化UI组件。 | |
| 72 | | [嵌入ArkTS组件](ndk-embed-arkts-components.md) | 介绍了如何在Native侧构建带有ArkTS组件的界面。 | | 72 | | [嵌入ArkTS组件](ndk-embed-arkts-components.md) | 介绍了如何在Native侧构建带有ArkTS组件的界面。 | |
| 73 | | [构建渲染节点](ndk-embed-render-components.md) | 介绍了如何在Native侧构建渲染节点。 | | 73 | | [构建渲染节点](ndk-embed-render-components.md) | 介绍了如何在Native侧构建渲染节点。 | |
| 74 | -| [通过自绘制接入无障碍](ndk-accessibility-xcomponent.md) | 介绍了自绘制机制接入的第三方UI框架平台,通过获取AccessibilityProvider如何对接系统无障碍。 | | 74 | +| [通过自绘制接入无障碍](ndk-accessibility-xcomponent.md) | 介绍了采用自绘制机制的第三方UI框架如何通过获取AccessibilityProvider对接系统无障碍能力。 | |
| 75 | | [自定义绘制](arkts-user-defined-draw.md) | 介绍了如何使用自定义绘制能力,实现自定义内容的绘制。 | | 75 | | [自定义绘制](arkts-user-defined-draw.md) | 介绍了如何使用自定义绘制能力,实现自定义内容的绘制。 | |
| 76 | | [查询和操作自定义节点](ndk-node-query-operate.md) | 介绍了如何对自定义节点进行查询和操作。 | | 76 | | [查询和操作自定义节点](ndk-node-query-operate.md) | 介绍了如何对自定义节点进行查询和操作。 | |
| 77 | | [通过EmbeddedComponent拉起EmbeddedUIExtensionAbility](ndk-embedded-component.md) | 介绍了如何在Native侧通过EmbeddedComponent拉起EmbeddedUIExtensionAbility。主要用于有进程隔离需求的模块化开发场景。 | | 77 | | [通过EmbeddedComponent拉起EmbeddedUIExtensionAbility](ndk-embedded-component.md) | 介绍了如何在Native侧通过EmbeddedComponent拉起EmbeddedUIExtensionAbility。主要用于有进程隔离需求的模块化开发场景。 | |
| @@ -82,4 +82,4 @@ ArkUI NDK接口能力主要包括: | |||
| 82 | 82 | ||
| 83 | ## 注意事项 | 83 | ## 注意事项 |
| 84 | 84 | ||
| 85 | -使用NDK接口开发UI界面时,需要保证相关UI接口调用在应用主线程上调用,避免多线程操作导致应用崩溃问题。 | 85 | +使用NDK接口开发UI界面时,应遵循各接口的线程约束;除明确支持多线程调用的接口外,需要在应用主线程调用相关UI接口,避免因违反接口线程约束而导致应用崩溃。 |
| @@ -90,9 +90,9 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 90 | // 定义创建函数的返回值,用于ArkTS侧和Native侧的交互。 | 90 | // 定义创建函数的返回值,用于ArkTS侧和Native侧的交互。 |
| 91 | interface MixedModuleResult { | 91 | interface MixedModuleResult { |
| 92 | // 定义针对Refresh构建函数的封装对象,用于Native侧转化为ArkUI_NodeHandle对象。 | 92 | // 定义针对Refresh构建函数的封装对象,用于Native侧转化为ArkUI_NodeHandle对象。 |
| 93 | - content?: ComponentContent<RefreshAttribute>; | 93 | + content: ComponentContent<RefreshAttribute>; |
| 94 | // Refresh作为容器组件,需要使用ContentSlot机制挂载Native侧的子组件。 | 94 | // Refresh作为容器组件,需要使用ContentSlot机制挂载Native侧的子组件。 |
| 95 | - childSlot?: NodeContent; | 95 | + childSlot: NodeContent; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | // 提供创建ArkTS组件的入口函数。 | 98 | // 提供创建ArkTS组件的入口函数。 |
| @@ -101,19 +101,19 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 101 | // 通过AppStorage对象在Ability启动的时候保持UI上下文对象。 | 101 | // 通过AppStorage对象在Ability启动的时候保持UI上下文对象。 |
| 102 | let uiContent = AppStorage.get<UIContext>('context'); | 102 | let uiContent = AppStorage.get<UIContext>('context'); |
| 103 | let modifier = new RefreshModifier(); | 103 | let modifier = new RefreshModifier(); |
| 104 | - if (value.width) { | 104 | + if (value.width !== undefined) { |
| 105 | modifier.width(value.width); | 105 | modifier.width(value.width); |
| 106 | } | 106 | } |
| 107 | - if (value.height) { | 107 | + if (value.height !== undefined) { |
| 108 | modifier.height(value.height); | 108 | modifier.height(value.height); |
| 109 | } | 109 | } |
| 110 | - if (value.backgroundColor) { | 110 | + if (value.backgroundColor !== undefined) { |
| 111 | modifier.backgroundColor(value.backgroundColor); | 111 | modifier.backgroundColor(value.backgroundColor); |
| 112 | } | 112 | } |
| 113 | - if (value.pullToRefresh) { | 113 | + if (value.pullToRefresh !== undefined) { |
| 114 | modifier.pullToRefresh(value.pullToRefresh); | 114 | modifier.pullToRefresh(value.pullToRefresh); |
| 115 | } | 115 | } |
| 116 | - if (value.refreshOffset) { | 116 | + if (value.refreshOffset !== undefined) { |
| 117 | modifier.refreshOffset(value.refreshOffset); | 117 | modifier.refreshOffset(value.refreshOffset); |
| 118 | } | 118 | } |
| 119 | // 创建NodeContent插槽对象用于Refresh子组件挂载。 | 119 | // 创建NodeContent插槽对象用于Refresh子组件挂载。 |
| @@ -132,23 +132,23 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | // 定义Refresh组件的更新函数,用于Native侧更新。 | 134 | // 定义Refresh组件的更新函数,用于Native侧更新。 |
| 135 | - // 在更新场景下,需要将Refresh组件的封装对象及其子组件插槽对象返回,防止组件重新创建。 | 135 | + // 在更新场景下,需要将Refresh组件的封装对象及其子组件插槽对象传入更新函数并复用,防止组件重新创建。 |
| 136 | export function updateMixedRefresh(refresh: ComponentContent<RefreshAttribute>, childSlot: NodeContent, | 136 | export function updateMixedRefresh(refresh: ComponentContent<RefreshAttribute>, childSlot: NodeContent, |
| 137 | value: NativeRefreshAttribute): void { | 137 | value: NativeRefreshAttribute): void { |
| 138 | let modifier = new RefreshModifier(); | 138 | let modifier = new RefreshModifier(); |
| 139 | - if (value.width) { | 139 | + if (value.width !== undefined) { |
| 140 | modifier.width(value.width); | 140 | modifier.width(value.width); |
| 141 | } | 141 | } |
| 142 | - if (value.height) { | 142 | + if (value.height !== undefined) { |
| 143 | modifier.height(value.height); | 143 | modifier.height(value.height); |
| 144 | } | 144 | } |
| 145 | - if (value.backgroundColor) { | 145 | + if (value.backgroundColor !== undefined) { |
| 146 | modifier.backgroundColor(value.backgroundColor); | 146 | modifier.backgroundColor(value.backgroundColor); |
| 147 | } | 147 | } |
| 148 | - if (value.pullToRefresh) { | 148 | + if (value.pullToRefresh !== undefined) { |
| 149 | modifier.pullToRefresh(value.pullToRefresh); | 149 | modifier.pullToRefresh(value.pullToRefresh); |
| 150 | } | 150 | } |
| 151 | - if (value.refreshOffset) { | 151 | + if (value.refreshOffset !== undefined) { |
| 152 | modifier.refreshOffset(value.refreshOffset); | 152 | modifier.refreshOffset(value.refreshOffset); |
| 153 | } | 153 | } |
| 154 | // 调用ComponentContent的update接口进行更新。 | 154 | // 调用ComponentContent的update接口进行更新。 |
| @@ -349,7 +349,7 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 349 | include_directories(${NATIVERENDER_ROOT_PATH} | 349 | include_directories(${NATIVERENDER_ROOT_PATH} |
| 350 | ${NATIVERENDER_ROOT_PATH}/include) | 350 | ${NATIVERENDER_ROOT_PATH}/include) |
| 351 | 351 | ||
| 352 | - add_library(entry SHARED NativeEntry.cpp ArkUIMixedRefresh.cpp NapiInit.cpp) | 352 | + add_library(entry SHARED NativeEntry.cpp ArkUIMixedRefresh.cpp NapiInit.cpp UITimer.cpp) |
| 353 | # target_link_libraries(entry PUBLIC libace_napi.z.so, libace_ndk.z.so, libhilog_ndk.z.so) | 353 | # target_link_libraries(entry PUBLIC libace_napi.z.so, libace_ndk.z.so, libhilog_ndk.z.so) |
| 354 | 354 | ||
| 355 | find_library( | 355 | find_library( |
| @@ -445,7 +445,7 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 445 | 445 | ||
| 446 | // 定义Native侧和ArkTS侧的交互数据结构。 | 446 | // 定义Native侧和ArkTS侧的交互数据结构。 |
| 447 | struct NativeRefreshAttribute { | 447 | struct NativeRefreshAttribute { |
| 448 | - std::optional<bool> isRefreshing; | 448 | + std::optional<bool> isRefreshing = false; |
| 449 | std::optional<float> width; | 449 | std::optional<float> width; |
| 450 | std::optional<float> height; | 450 | std::optional<float> height; |
| 451 | std::optional<uint32_t> backgroundColor; | 451 | std::optional<uint32_t> backgroundColor; |
| @@ -464,9 +464,15 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 464 | napi_ref componentContent, napi_ref nodeContent) | 464 | napi_ref componentContent, napi_ref nodeContent) |
| 465 | : ArkUIMixedNode(handle, env, componentContent), contentHandle_(contentHandle), nodeContent_(nodeContent) {} | 465 | : ArkUIMixedNode(handle, env, componentContent), contentHandle_(contentHandle), nodeContent_(nodeContent) {} |
| 466 | 466 | ||
| 467 | - ArkUIMixedRefresh() : ArkUIMixedNode(nullptr, nullptr, nullptr) {} | 467 | + ArkUIMixedRefresh() |
| 468 | + : ArkUIMixedNode(nullptr, nullptr, nullptr), contentHandle_(nullptr), nodeContent_(nullptr) {} | ||
| 468 | 469 | ||
| 469 | - ~ArkUIMixedRefresh() override { napi_delete_reference(env_, nodeContent_); } // 释放子节点占位组件插槽对象。 | 470 | + ~ArkUIMixedRefresh() override |
| 471 | + { | ||
| 472 | + if (env_ != nullptr && nodeContent_ != nullptr) { | ||
| 473 | + napi_delete_reference(env_, nodeContent_); | ||
| 474 | + } | ||
| 475 | + } // 释放子节点占位组件插槽对象。 | ||
| 470 | 476 | ||
| 471 | void SetWidth(float width) { attribute_.width = width; } | 477 | void SetWidth(float width) { attribute_.width = width; } |
| 472 | 478 | ||
| @@ -569,12 +575,12 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 569 | } | 575 | } |
| 570 | if (attribute.pullToRefresh) { | 576 | if (attribute.pullToRefresh) { |
| 571 | napi_value pullToRefresh; | 577 | napi_value pullToRefresh; |
| 572 | - napi_create_int32(g_env, attribute.pullToRefresh.value(), &pullToRefresh); | 578 | + napi_get_boolean(g_env, attribute.pullToRefresh.value(), &pullToRefresh); |
| 573 | desc[REFRESH_OFFSET_INDEX3].value = pullToRefresh; | 579 | desc[REFRESH_OFFSET_INDEX3].value = pullToRefresh; |
| 574 | } | 580 | } |
| 575 | if (attribute.isRefreshing) { | 581 | if (attribute.isRefreshing) { |
| 576 | napi_value isRefreshing; | 582 | napi_value isRefreshing; |
| 577 | - napi_create_int32(g_env, attribute.isRefreshing.value(), &isRefreshing); | 583 | + napi_get_boolean(g_env, attribute.isRefreshing.value(), &isRefreshing); |
| 578 | desc[REFRESH_OFFSET_INDEX4].value = isRefreshing; | 584 | desc[REFRESH_OFFSET_INDEX4].value = isRefreshing; |
| 579 | } | 585 | } |
| 580 | if (attribute.refreshOffset) { | 586 | if (attribute.refreshOffset) { |
| @@ -659,13 +665,21 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 659 | // 获取ArkTS的Refresh组件。 | 665 | // 获取ArkTS的Refresh组件。 |
| 660 | napi_value componentContent = nullptr; | 666 | napi_value componentContent = nullptr; |
| 661 | napi_get_named_property(g_env, result, "content", &componentContent); | 667 | napi_get_named_property(g_env, result, "content", &componentContent); |
| 662 | - ArkUI_NodeHandle handle; | 668 | + ArkUI_NodeHandle handle = nullptr; |
| 663 | - OH_ArkUI_GetNodeHandleFromNapiValue(g_env, componentContent, &handle); | 669 | + auto code = OH_ArkUI_GetNodeHandleFromNapiValue(g_env, componentContent, &handle); |
| 670 | + if (code != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 671 | + napi_close_handle_scope(g_env, scope); | ||
| 672 | + return nullptr; | ||
| 673 | + } | ||
| 664 | // 获取ArkTS的Refresh组件的子组件插槽。 | 674 | // 获取ArkTS的Refresh组件的子组件插槽。 |
| 665 | napi_value nodeContent = nullptr; | 675 | napi_value nodeContent = nullptr; |
| 666 | napi_get_named_property(g_env, result, "childSlot", &nodeContent); | 676 | napi_get_named_property(g_env, result, "childSlot", &nodeContent); |
| 667 | - ArkUI_NodeContentHandle contentHandle; | 677 | + ArkUI_NodeContentHandle contentHandle = nullptr; |
| 668 | - OH_ArkUI_GetNodeContentFromNapiValue(g_env, nodeContent, &contentHandle); | 678 | + code = OH_ArkUI_GetNodeContentFromNapiValue(g_env, nodeContent, &contentHandle); |
| 679 | + if (code != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 680 | + napi_close_handle_scope(g_env, scope); | ||
| 681 | + return nullptr; | ||
| 682 | + } | ||
| 669 | // 保存ArkTS的ComponentContent用于防止ArkTS侧对象释放以及后续的更新。 | 683 | // 保存ArkTS的ComponentContent用于防止ArkTS侧对象释放以及后续的更新。 |
| 670 | napi_ref componentContentRef; | 684 | napi_ref componentContentRef; |
| 671 | napi_create_reference(g_env, componentContent, 1, &componentContentRef); | 685 | napi_create_reference(g_env, componentContent, 1, &componentContentRef); |
| @@ -742,83 +756,123 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 742 | ``` | 756 | ``` |
| 743 | 757 | ||
| 744 | 6. 定时器模块相关简单实现。 | 758 | 6. 定时器模块相关简单实现。 |
| 745 | - <!-- @[ui_timer](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NativeType/NdkEmbedArktsComponents/entry/src/main/cpp/UITimer.h) --> | 759 | + <!-- @[ui_timer](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NativeType/NdkEmbedArktsComponents/entry/src/main/cpp/UITimer.cpp) --> |
| 746 | 760 | ||
| 747 | - ``` C | 761 | + ``` C++ |
| 748 | - // UITimer.h | 762 | + // UITimer.cpp |
| 749 | // 定时器模块。 | 763 | // 定时器模块。 |
| 750 | 764 | ||
| 751 | - #ifndef MYAPPLICATION_UITIMER_H | 765 | + #include "UITimer.h" |
| 752 | - #define MYAPPLICATION_UITIMER_H | ||
| 753 | 766 | ||
| 754 | #include <hilog/log.h> | 767 | #include <hilog/log.h> |
| 755 | - #include <js_native_api.h> | ||
| 756 | - #include <js_native_api_types.h> | ||
| 757 | - #include <node_api.h> | ||
| 758 | - #include <node_api_types.h> | ||
| 759 | #include <string> | 768 | #include <string> |
| 760 | #include <thread> | 769 | #include <thread> |
| 761 | #include <uv.h> | 770 | #include <uv.h> |
| 762 | 771 | ||
| 763 | namespace NativeModule { | 772 | namespace NativeModule { |
| 764 | - | 773 | + namespace { |
| 765 | struct UIData { | 774 | struct UIData { |
| 766 | - void *userData = nullptr; | 775 | + std::weak_ptr<ArkUIMixedRefresh> refresh; |
| 767 | int32_t count = 0; | 776 | int32_t count = 0; |
| 768 | int32_t totalCount = 0; | 777 | int32_t totalCount = 0; |
| 769 | - void (*func)(void *userData, int32_t count) = nullptr; | 778 | + TimerCallback func = nullptr; |
| 779 | + napi_threadsafe_function threadSafeFunction = nullptr; | ||
| 770 | }; | 780 | }; |
| 771 | 781 | ||
| 772 | - napi_threadsafe_function threadSafeFunction = nullptr; | 782 | + void DeleteTimerHandle(uv_handle_t *handle) |
| 773 | - | ||
| 774 | - void CreateNativeTimer(napi_env env, void *userData, int32_t totalCount, void (*func)(void *userData, int32_t count)) | ||
| 775 | { | 783 | { |
| 776 | - napi_value name; | 784 | + delete reinterpret_cast<uv_timer_t *>(handle); |
| 777 | - std::string str = "UICallback"; | 785 | + } |
| 778 | - napi_create_string_utf8(env, str.c_str(), str.size(), &name); | 786 | + |
| 779 | - // UI主线程回调函数。 | 787 | + void CloseTimer(uv_timer_t *timer) |
| 780 | - napi_create_threadsafe_function( | 788 | + { |
| 781 | - env, nullptr, nullptr, name, 0, 1, nullptr, nullptr, nullptr, | 789 | + uv_timer_stop(timer); |
| 782 | - [](napi_env env, napi_value value, void *context, void *data) { | 790 | + uv_close(reinterpret_cast<uv_handle_t *>(timer), DeleteTimerHandle); |
| 783 | - auto userdata = reinterpret_cast<UIData *>(data); | 791 | + } |
| 784 | - userdata->func(userdata->userData, userdata->count); | 792 | + |
| 785 | - delete userdata; | 793 | + void CallRefreshOnUI(napi_env, napi_value, void *, void *data) |
| 786 | - }, | 794 | + { |
| 787 | - &threadSafeFunction); | 795 | + auto *callbackData = reinterpret_cast<UIData *>(data); |
| 788 | - // 启动定时器,模拟数据变化。 | 796 | + auto refresh = callbackData->refresh.lock(); |
| 789 | - std::thread timerThread([data = userData, totalCount, func]() { | 797 | + if (refresh != nullptr && callbackData->func != nullptr) { |
| 790 | - uv_loop_t *loop = uv_loop_new(); | 798 | + callbackData->func(refresh, callbackData->count); |
| 791 | - uv_timer_t *timer = new uv_timer_t(); | 799 | + } |
| 792 | - uv_timer_init(loop, timer); | 800 | + delete callbackData; |
| 793 | - timer->data = new UIData{data, 0, totalCount, func}; | 801 | + } |
| 794 | - uint64_t timeout = 4000; | 802 | + |
| 795 | - uint64_t repeat = 4000; | 803 | + napi_status CreateTimerThreadSafeFunction(napi_env env, UIData *customData) |
| 796 | - uv_timer_start( | 804 | + { |
| 797 | - timer, | 805 | + napi_value name = nullptr; |
| 798 | - [](uv_timer_t *handle) { | 806 | + const std::string callbackName = "UICallback"; |
| 799 | - OH_LOG_INFO(LOG_APP, "on timeout"); | 807 | + auto status = napi_create_string_utf8(env, callbackName.c_str(), callbackName.size(), &name); |
| 800 | - napi_acquire_threadsafe_function(threadSafeFunction); | 808 | + if (status != napi_ok) { |
| 801 | - auto *customData = reinterpret_cast<UIData *>(handle->data); | 809 | + return status; |
| 802 | - // 创建回调数据。 | 810 | + } |
| 803 | - auto *callbackData = | 811 | + return napi_create_threadsafe_function(env, nullptr, nullptr, name, 0, 1, nullptr, nullptr, nullptr, |
| 804 | - new UIData{customData->userData, customData->count, customData->totalCount, customData->func}; | 812 | + CallRefreshOnUI, &customData->threadSafeFunction); |
| 805 | - napi_call_threadsafe_function(threadSafeFunction, callbackData, napi_tsfn_blocking); | 813 | + } |
| 806 | - customData->count++; | 814 | + |
| 807 | - if (customData->count > customData->totalCount) { | 815 | + void OnTimer(uv_timer_t *timer) |
| 808 | - uv_timer_stop(handle); | 816 | + { |
| 809 | - delete handle; | 817 | + OH_LOG_INFO(LOG_APP, "on timeout"); |
| 810 | - delete customData; | 818 | + auto *customData = reinterpret_cast<UIData *>(timer->data); |
| 811 | - } | 819 | + auto *callbackData = new UIData(*customData); |
| 812 | - }, | 820 | + if (napi_call_threadsafe_function(customData->threadSafeFunction, callbackData, |
| 813 | - timeout, repeat); | 821 | + napi_tsfn_blocking) != napi_ok) { |
| 814 | - uv_run(loop, UV_RUN_DEFAULT); | 822 | + delete callbackData; |
| 823 | + } | ||
| 824 | + customData->count++; | ||
| 825 | + if (customData->count <= customData->totalCount) { | ||
| 826 | + return; | ||
| 827 | + } | ||
| 828 | + CloseTimer(timer); | ||
| 829 | + } | ||
| 830 | + | ||
| 831 | + void ReleaseTimerData(UIData *customData) | ||
| 832 | + { | ||
| 833 | + napi_release_threadsafe_function(customData->threadSafeFunction, napi_tsfn_release); | ||
| 834 | + delete customData; | ||
| 835 | + } | ||
| 836 | + | ||
| 837 | + void RunTimer(UIData *customData) | ||
| 838 | + { | ||
| 839 | + uv_loop_t *loop = uv_loop_new(); | ||
| 840 | + if (loop == nullptr) { | ||
| 841 | + ReleaseTimerData(customData); | ||
| 842 | + return; | ||
| 843 | + } | ||
| 844 | + auto *timer = new uv_timer_t(); | ||
| 845 | + if (uv_timer_init(loop, timer) != 0) { | ||
| 846 | + delete timer; | ||
| 847 | + ReleaseTimerData(customData); | ||
| 815 | uv_loop_delete(loop); | 848 | uv_loop_delete(loop); |
| 816 | - }); | 849 | + return; |
| 850 | + } | ||
| 851 | + timer->data = customData; | ||
| 852 | + constexpr uint64_t timeout = 4000; | ||
| 853 | + constexpr uint64_t repeat = 4000; | ||
| 854 | + if (uv_timer_start(timer, OnTimer, timeout, repeat) != 0) { | ||
| 855 | + CloseTimer(timer); | ||
| 856 | + } | ||
| 857 | + uv_run(loop, UV_RUN_DEFAULT); | ||
| 858 | + ReleaseTimerData(customData); | ||
| 859 | + uv_loop_delete(loop); | ||
| 860 | + } | ||
| 861 | + } // namespace | ||
| 862 | + | ||
| 863 | + void CreateNativeTimer(napi_env env, const std::weak_ptr<ArkUIMixedRefresh> &refresh, int32_t totalCount, | ||
| 864 | + TimerCallback func) | ||
| 865 | + { | ||
| 866 | + auto *customData = new UIData {refresh, 0, totalCount, func, nullptr}; | ||
| 867 | + if (CreateTimerThreadSafeFunction(env, customData) != napi_ok) { | ||
| 868 | + delete customData; | ||
| 869 | + return; | ||
| 870 | + } | ||
| 871 | + // 启动定时器,模拟数据变化。 | ||
| 872 | + std::thread timerThread(RunTimer, customData); | ||
| 817 | timerThread.detach(); | 873 | timerThread.detach(); |
| 818 | } | 874 | } |
| 819 | } // namespace NativeModule | 875 | } // namespace NativeModule |
| 820 | - | ||
| 821 | - #endif // MYAPPLICATION_UITIMER_H | ||
| 822 | ``` | 876 | ``` |
| 823 | 877 | ||
| 824 | 7. 使用[接入ArkTS页面](ndk-access-the-arkts-page.md)章节的页面结构,将Refresh组件作为文本列表的父组件。 | 878 | 7. 使用[接入ArkTS页面](ndk-access-the-arkts-page.md)章节的页面结构,将Refresh组件作为文本列表的父组件。 |
| @@ -847,20 +901,26 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 847 | NativeRefreshAttribute nativeRefreshAttribute{ | 901 | NativeRefreshAttribute nativeRefreshAttribute{ |
| 848 | .backgroundColor = 0xFF89CFF0, .refreshOffset = 64, .pullToRefresh = true}; | 902 | .backgroundColor = 0xFF89CFF0, .refreshOffset = 64, .pullToRefresh = true}; |
| 849 | auto refresh = ArkUIMixedRefresh::Create(nativeRefreshAttribute); | 903 | auto refresh = ArkUIMixedRefresh::Create(nativeRefreshAttribute); |
| 904 | + if (refresh == nullptr) { | ||
| 905 | + return nullptr; | ||
| 906 | + } | ||
| 850 | refresh->AddChild(list); | 907 | refresh->AddChild(list); |
| 851 | 908 | ||
| 852 | // 设置混合模式下的事件。 | 909 | // 设置混合模式下的事件。 |
| 853 | refresh->SetOnOffsetChange( | 910 | refresh->SetOnOffsetChange( |
| 854 | [](float offset) { OH_LOG_INFO(LOG_APP, "on refresh offset changed: %{public}f", offset); }); | 911 | [](float offset) { OH_LOG_INFO(LOG_APP, "on refresh offset changed: %{public}f", offset); }); |
| 855 | - refresh->SetRefreshCallback([refreshPtr = refresh.get(), env]() { | 912 | + std::weak_ptr<ArkUIMixedRefresh> weakRefresh = refresh; |
| 913 | + refresh->SetRefreshCallback([weakRefresh, env]() { | ||
| 856 | OH_LOG_INFO(LOG_APP, "on refreshing"); | 914 | OH_LOG_INFO(LOG_APP, "on refreshing"); |
| 857 | - // 启动定时器,模拟数据获取。 | 915 | + if (auto refresh = weakRefresh.lock()) { |
| 858 | - CreateNativeTimer(env, refreshPtr, 1, [](void *userData, int32_t count) { | 916 | + // 启动定时器,模拟数据获取;定时器只保存弱引用,避免访问已销毁的Refresh对象。 |
| 859 | - // 数据获取后关闭刷新。 | 917 | + CreateNativeTimer(env, weakRefresh, 1, |
| 860 | - auto refresh = reinterpret_cast<ArkUIMixedRefresh *>(userData); | 918 | + [](const std::shared_ptr<ArkUIMixedRefresh> &refresh, int32_t count) { |
| 861 | - refresh->SetRefreshState(false); | 919 | + // 数据获取后关闭刷新。 |
| 862 | - refresh->FlushMixedModeCmd(); | 920 | + refresh->SetRefreshState(false); |
| 863 | - }); | 921 | + refresh->FlushMixedModeCmd(); |
| 922 | + }); | ||
| 923 | + } | ||
| 864 | }); | 924 | }); |
| 865 | 925 | ||
| 866 | // 更新事件到ArkTS侧。 | 926 | // 更新事件到ArkTS侧。 |
| @@ -901,12 +961,18 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 901 | napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); | 961 | napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); |
| 902 | 962 | ||
| 903 | // 获取NodeContent | 963 | // 获取NodeContent |
| 904 | - ArkUI_NodeContentHandle contentHandle; | 964 | + ArkUI_NodeContentHandle contentHandle = nullptr; |
| 905 | - OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); | 965 | + auto code = OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); |
| 966 | + if (code != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 967 | + return nullptr; | ||
| 968 | + } | ||
| 906 | NativeEntry::GetInstance()->SetContentHandle(contentHandle); | 969 | NativeEntry::GetInstance()->SetContentHandle(contentHandle); |
| 907 | 970 | ||
| 908 | // 创建Refresh文本列表 | 971 | // 创建Refresh文本列表 |
| 909 | auto refresh = CreateMixedRefreshList(env); | 972 | auto refresh = CreateMixedRefreshList(env); |
| 973 | + if (refresh == nullptr) { | ||
| 974 | + return nullptr; | ||
| 975 | + } | ||
| 910 | 976 | ||
| 911 | // 保持Native侧对象到管理类中,维护生命周期。 | 977 | // 保持Native侧对象到管理类中,维护生命周期。 |
| 912 | NativeEntry::GetInstance()->SetRootNode(refresh); | 978 | NativeEntry::GetInstance()->SetRootNode(refresh); |
| @@ -927,9 +993,38 @@ ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Nati | |||
| 927 | <!-- @[bridge_index](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NativeType/NdkEmbedArktsComponents/entry/src/main/cpp/types/libentry/Index.d.ts) --> | 993 | <!-- @[bridge_index](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NativeType/NdkEmbedArktsComponents/entry/src/main/cpp/types/libentry/Index.d.ts) --> |
| 928 | 994 | ||
| 929 | ``` TypeScript | 995 | ``` TypeScript |
| 930 | - export const createNativeRoot: (content: Object) => void; | 996 | + import { ComponentContent, NodeContent, RefreshModifier } from '@kit.ArkUI'; |
| 997 | + | ||
| 998 | + interface NativeRefreshAttribute { | ||
| 999 | + isRefreshing: boolean; | ||
| 1000 | + width?: number; | ||
| 1001 | + height?: number; | ||
| 1002 | + backgroundColor?: number; | ||
| 1003 | + refreshOffset?: number; | ||
| 1004 | + pullToRefresh?: boolean; | ||
| 1005 | + onRefreshing?: () => void; | ||
| 1006 | + onOffsetChange?: (offset: number) => void; | ||
| 1007 | + } | ||
| 1008 | + | ||
| 1009 | + interface RefreshAttribute { | ||
| 1010 | + isRefreshing: boolean; | ||
| 1011 | + modifier?: RefreshModifier; | ||
| 1012 | + slot?: NodeContent; | ||
| 1013 | + onRefreshing?: () => void; | ||
| 1014 | + onOffsetChange?: (offset: number) => void; | ||
| 1015 | + } | ||
| 1016 | + | ||
| 1017 | + interface MixedModuleResult { | ||
| 1018 | + content: ComponentContent<RefreshAttribute>; | ||
| 1019 | + childSlot: NodeContent; | ||
| 1020 | + } | ||
| 1021 | + | ||
| 1022 | + export const createNativeRoot: (content: NodeContent) => void; | ||
| 931 | export const destroyNativeRoot: () => void; | 1023 | export const destroyNativeRoot: () => void; |
| 932 | - | 1024 | + |
| 933 | - export const registerCreateMixedRefreshNode: (content: Object) => void; | 1025 | + export const registerCreateMixedRefreshNode: ( |
| 934 | - export const registerUpdateMixedRefreshNode: (content: Object) => void; | 1026 | + callback: (value: NativeRefreshAttribute) => MixedModuleResult) => void; |
| 1027 | + export const registerUpdateMixedRefreshNode: ( | ||
| 1028 | + callback: (refresh: ComponentContent<RefreshAttribute>, childSlot: NodeContent, | ||
| 1029 | + value: NativeRefreshAttribute) => void) => void; | ||
| 935 | ``` | 1030 | ``` |
| @@ -43,6 +43,32 @@ | |||
| 43 | #include <native_drawing/drawing_path.h> | 43 | #include <native_drawing/drawing_path.h> |
| 44 | #include <native_drawing/drawing_pen.h> | 44 | #include <native_drawing/drawing_pen.h> |
| 45 | 45 | ||
| 46 | + // 渲染节点需要在宿主节点销毁后显式释放,避免只销毁FrameNode而泄漏RenderNode。 | ||
| 47 | + struct BasicRenderNodeResources { | ||
| 48 | + ArkUI_RenderNodeHandle renderRootNode = nullptr; | ||
| 49 | + ArkUI_RenderNodeHandle firstChildRenderNode = nullptr; | ||
| 50 | + ArkUI_RenderNodeHandle secondChildRenderNode = nullptr; | ||
| 51 | + ArkUI_RenderNodeHandle thirdChildRenderNode = nullptr; | ||
| 52 | + | ||
| 53 | + ~BasicRenderNodeResources() | ||
| 54 | + { | ||
| 55 | + if (thirdChildRenderNode != nullptr) { | ||
| 56 | + OH_ArkUI_RenderNodeUtils_DisposeNode(thirdChildRenderNode); | ||
| 57 | + } | ||
| 58 | + if (secondChildRenderNode != nullptr) { | ||
| 59 | + OH_ArkUI_RenderNodeUtils_DisposeNode(secondChildRenderNode); | ||
| 60 | + } | ||
| 61 | + if (firstChildRenderNode != nullptr) { | ||
| 62 | + OH_ArkUI_RenderNodeUtils_DisposeNode(firstChildRenderNode); | ||
| 63 | + } | ||
| 64 | + if (renderRootNode != nullptr) { | ||
| 65 | + OH_ArkUI_RenderNodeUtils_DisposeNode(renderRootNode); | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + }; | ||
| 69 | + | ||
| 70 | + static BasicRenderNodeResources *g_basicRenderNodeResources = nullptr; | ||
| 71 | + | ||
| 46 | ArkUI_NodeHandle testRenderNode(ArkUI_NativeNodeAPI_1 *nodeAPI) { | 72 | ArkUI_NodeHandle testRenderNode(ArkUI_NativeNodeAPI_1 *nodeAPI) { |
| 47 | // 创建NDK原有容器逻辑。 | 73 | // 创建NDK原有容器逻辑。 |
| 48 | ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); | 74 | ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); |
| @@ -56,7 +82,7 @@ | |||
| 56 | nodeAPI->setAttribute(column, NODE_WIDTH, &itemWidth); | 82 | nodeAPI->setAttribute(column, NODE_WIDTH, &itemWidth); |
| 57 | nodeAPI->setAttribute(column, NODE_HEIGHT, &itemHeight); | 83 | nodeAPI->setAttribute(column, NODE_HEIGHT, &itemHeight); |
| 58 | ArkUI_NodeHandle text = nodeAPI->createNode(ARKUI_NODE_TEXT); | 84 | ArkUI_NodeHandle text = nodeAPI->createNode(ARKUI_NODE_TEXT); |
| 59 | - ArkUI_AttributeItem content = {.string = "黄色背景是C-API页面"}; | 85 | + ArkUI_AttributeItem content = {.string = "这是C-API页面"}; |
| 60 | nodeAPI->setAttribute(text, NODE_TEXT_CONTENT, &content); | 86 | nodeAPI->setAttribute(text, NODE_TEXT_CONTENT, &content); |
| 61 | nodeAPI->addChild(column, text); | 87 | nodeAPI->addChild(column, text); |
| 62 | 88 | ||
| @@ -73,10 +99,16 @@ | |||
| 73 | auto firstChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); | 99 | auto firstChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); |
| 74 | auto secondChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); | 100 | auto secondChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); |
| 75 | auto thirdChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); | 101 | auto thirdChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); |
| 102 | + | ||
| 103 | + delete g_basicRenderNodeResources; | ||
| 104 | + g_basicRenderNodeResources = new BasicRenderNodeResources { | ||
| 105 | + renderRootNode, firstChildRenderNode, secondChildRenderNode, thirdChildRenderNode}; | ||
| 76 | 106 | ||
| 77 | auto result = OH_ArkUI_RenderNodeUtils_AddRenderNode(Custom, renderRootNode); | 107 | auto result = OH_ArkUI_RenderNodeUtils_AddRenderNode(Custom, renderRootNode); |
| 78 | if (result != ARKUI_ERROR_CODE_NO_ERROR) { | 108 | if (result != ARKUI_ERROR_CODE_NO_ERROR) { |
| 79 | // 通过错误码判断根节点是否挂载成功。 | 109 | // 通过错误码判断根节点是否挂载成功。 |
| 110 | + delete g_basicRenderNodeResources; | ||
| 111 | + g_basicRenderNodeResources = nullptr; | ||
| 80 | return scroll; | 112 | return scroll; |
| 81 | } | 113 | } |
| 82 | 114 | ||
| @@ -149,6 +181,14 @@ | |||
| 149 | NativeEntry::GetInstance()->SetRootNode(testNode); | 181 | NativeEntry::GetInstance()->SetRootNode(testNode); |
| 150 | return nullptr; | 182 | return nullptr; |
| 151 | } | 183 | } |
| 184 | + | ||
| 185 | + napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { | ||
| 186 | + // 先销毁宿主节点,再释放其挂载的RenderNode树。 | ||
| 187 | + NativeEntry::GetInstance()->DisposeRootNode(); | ||
| 188 | + delete g_basicRenderNodeResources; | ||
| 189 | + g_basicRenderNodeResources = nullptr; | ||
| 190 | + return nullptr; | ||
| 191 | + } | ||
| 152 | 192 | ||
| 153 | ``` | 193 | ``` |
| 154 | 194 | ||
| @@ -214,15 +254,17 @@ | |||
| 214 | 254 | ||
| 215 | ArkUI_NodeHandle testRenderNode2(ArkUI_NativeNodeAPI_1 *nodeAPI, ArkUI_ContextHandle context) { | 255 | ArkUI_NodeHandle testRenderNode2(ArkUI_NativeNodeAPI_1 *nodeAPI, ArkUI_ContextHandle context) { |
| 216 | 256 | ||
| 217 | - ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_COLUMN); | 257 | + ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); |
| 218 | ArkUI_NumberValue valueWidth[] = {400}; | 258 | ArkUI_NumberValue valueWidth[] = {400}; |
| 219 | ArkUI_AttributeItem itemWidth = {valueWidth, sizeof(valueWidth) / sizeof(ArkUI_NumberValue)}; | 259 | ArkUI_AttributeItem itemWidth = {valueWidth, sizeof(valueWidth) / sizeof(ArkUI_NumberValue)}; |
| 220 | nodeAPI->setAttribute(scroll, NODE_WIDTH, &itemWidth); | 260 | nodeAPI->setAttribute(scroll, NODE_WIDTH, &itemWidth); |
| 221 | ArkUI_NumberValue valueHeight[] = {600}; | 261 | ArkUI_NumberValue valueHeight[] = {600}; |
| 222 | ArkUI_AttributeItem itemHeight = {valueHeight, sizeof(valueHeight) / sizeof(ArkUI_NumberValue)}; | 262 | ArkUI_AttributeItem itemHeight = {valueHeight, sizeof(valueHeight) / sizeof(ArkUI_NumberValue)}; |
| 223 | nodeAPI->setAttribute(scroll, NODE_HEIGHT, &itemHeight); | 263 | nodeAPI->setAttribute(scroll, NODE_HEIGHT, &itemHeight); |
| 224 | - valueHeight[0].u32 = 0xff00F100; | 264 | + ArkUI_NumberValue valueBackgroundColor[] = {{.u32 = 0xff00F100}}; |
| 225 | - nodeAPI->setAttribute(scroll, NODE_BACKGROUND_COLOR, &itemHeight); | 265 | + ArkUI_AttributeItem itemBackgroundColor = { |
| 266 | + valueBackgroundColor, sizeof(valueBackgroundColor) / sizeof(ArkUI_NumberValue)}; | ||
| 267 | + nodeAPI->setAttribute(scroll, NODE_BACKGROUND_COLOR, &itemBackgroundColor); | ||
| 226 | 268 | ||
| 227 | ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); | 269 | ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); |
| 228 | ArkUI_NodeHandle text = nodeAPI->createNode(ARKUI_NODE_TEXT); | 270 | ArkUI_NodeHandle text = nodeAPI->createNode(ARKUI_NODE_TEXT); |
| @@ -244,7 +286,7 @@ | |||
| 244 | resources->v2 = OH_ArkUI_RenderNodeUtils_CreateVector2AnimatableProperty(1000, 1000); | 286 | resources->v2 = OH_ArkUI_RenderNodeUtils_CreateVector2AnimatableProperty(1000, 1000); |
| 245 | resources->color = OH_ArkUI_RenderNodeUtils_CreateColorAnimatableProperty(0xFFFF11FF); | 287 | resources->color = OH_ArkUI_RenderNodeUtils_CreateColorAnimatableProperty(0xFFFF11FF); |
| 246 | 288 | ||
| 247 | - // 关联组件和多个modifier。 | 289 | + // 将渲染节点与一个modifier关联。 |
| 248 | resources->modifier = OH_ArkUI_RenderNodeUtils_CreateContentModifier(); | 290 | resources->modifier = OH_ArkUI_RenderNodeUtils_CreateContentModifier(); |
| 249 | OH_ArkUI_RenderNodeUtils_AttachContentModifier(resources->renderNode, resources->modifier); | 291 | OH_ArkUI_RenderNodeUtils_AttachContentModifier(resources->renderNode, resources->modifier); |
| 250 | // 关联modifier和property。 | 292 | // 关联modifier和property。 |
| @@ -392,7 +434,7 @@ | |||
| 392 | 434 | ||
| 393 | 节点处于被接纳的附属节点状态下,允许调用[OH_ArkUI_RenderNodeUtils_GetRenderNode](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_getrendernode)获取它对应的RenderNode。 | 435 | 节点处于被接纳的附属节点状态下,允许调用[OH_ArkUI_RenderNodeUtils_GetRenderNode](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_getrendernode)获取它对应的RenderNode。 |
| 394 | 436 | ||
| 395 | -调用[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md)的[disposeNode](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#disposenode)接口主动销毁父节点时,需要额外调用[OH_ArkUI_RenderNodeUtils_DisposeNode](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_disposenode)释放该渲染节点,否则会发生内存泄漏。 | 437 | +调用[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md)的[disposeNode](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#disposenode)接口主动销毁该附属节点对应的FrameNode时,需要额外调用[OH_ArkUI_RenderNodeUtils_DisposeNode](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_disposenode)释放该渲染节点,否则会发生内存泄漏。 |
| 396 | 438 | ||
| 397 | 439 | ||
| 398 | ### 操作来自附属节点的渲染节点 | 440 | ### 操作来自附属节点的渲染节点 |
| @@ -496,7 +538,6 @@ | |||
| 496 | // 定义传递参数的接口 | 538 | // 定义传递参数的接口 |
| 497 | interface ParamsInterface { | 539 | interface ParamsInterface { |
| 498 | text: string; | 540 | text: string; |
| 499 | - func: Function; | ||
| 500 | } | 541 | } |
| 501 | class MyNodeController extends NodeController { | 542 | class MyNodeController extends NodeController { |
| 502 | private imperativeNode: FrameNode | null = null; | 543 | private imperativeNode: FrameNode | null = null; |
| @@ -515,27 +556,25 @@ | |||
| 515 | let buildNode = new BuilderNode<[ParamsInterface]>(uiContext); | 556 | let buildNode = new BuilderNode<[ParamsInterface]>(uiContext); |
| 516 | // 创建节点树 | 557 | // 创建节点树 |
| 517 | buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), { | 558 | buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), { |
| 518 | - text: message, func: () => { | 559 | + text: message |
| 519 | - return 'FUNCTION'; | ||
| 520 | - } | ||
| 521 | }, { nestingBuilderSupported: true }); | 560 | }, { nestingBuilderSupported: true }); |
| 522 | this.buildNode = buildNode.getFrameNode(); | 561 | this.buildNode = buildNode.getFrameNode(); |
| 523 | entry.adopt(buildNode); | 562 | entry.adopt(buildNode); |
| 524 | } | 563 | } |
| 525 | - removeAdoptedNode(uiContext:UIContext):void { | 564 | + removeAdoptedNode(): void { |
| 526 | entry.removeAdopt(); | 565 | entry.removeAdopt(); |
| 527 | } | 566 | } |
| 528 | } | 567 | } |
| 529 | 568 | ||
| 530 | @Builder | 569 | @Builder |
| 531 | - function buildTextWithFunc(fun: Function) { | 570 | + function buildTextWithFunc() { |
| 532 | Web({ src: 'https://www.example.com', controller: new webview.WebviewController() }) | 571 | Web({ src: 'https://www.example.com', controller: new webview.WebviewController() }) |
| 533 | } | 572 | } |
| 534 | 573 | ||
| 535 | @Builder | 574 | @Builder |
| 536 | function buildText(params: ParamsInterface) { | 575 | function buildText(params: ParamsInterface) { |
| 537 | Column() { | 576 | Column() { |
| 538 | - buildTextWithFunc(params.func) | 577 | + buildTextWithFunc() |
| 539 | } | 578 | } |
| 540 | } | 579 | } |
| 541 | 580 | ||
| @@ -585,7 +624,7 @@ | |||
| 585 | Button(this.adoptmsg) | 624 | Button(this.adoptmsg) |
| 586 | .onClick(() => { | 625 | .onClick(() => { |
| 587 | if (this.isAdopt) { | 626 | if (this.isAdopt) { |
| 588 | - this.myNodeController.removeAdoptedNode(this.getUIContext()); | 627 | + this.myNodeController.removeAdoptedNode(); |
| 589 | this.adoptmsg = 'adopt web component'; | 628 | this.adoptmsg = 'adopt web component'; |
| 590 | } else { | 629 | } else { |
| 591 | this.myNodeController.adoptNode(this.getUIContext(),this.message); | 630 | this.myNodeController.adoptNode(this.getUIContext(),this.message); |
| @@ -613,14 +652,27 @@ | |||
| 613 | size_t argc = 1; | 652 | size_t argc = 1; |
| 614 | napi_value args[1] = {nullptr}; | 653 | napi_value args[1] = {nullptr}; |
| 615 | napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); | 654 | napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); |
| 616 | - // 获取ArkTS侧组件挂载点。 | 655 | + // 获取ArkTS侧节点句柄。 |
| 617 | int32_t result = OH_ArkUI_GetNodeHandleFromNapiValue(env, args[0], &nodeHandle_); | 656 | int32_t result = OH_ArkUI_GetNodeHandleFromNapiValue(env, args[0], &nodeHandle_); |
| 618 | if (result != ARKUI_ERROR_CODE_NO_ERROR) { | 657 | if (result != ARKUI_ERROR_CODE_NO_ERROR) { |
| 619 | return nullptr; | 658 | return nullptr; |
| 620 | } | 659 | } |
| 621 | result = OH_ArkUI_NativeModule_AdoptChild(custom_->GetHandle(), nodeHandle_); | 660 | result = OH_ArkUI_NativeModule_AdoptChild(custom_->GetHandle(), nodeHandle_); |
| 622 | - OH_ArkUI_RenderNodeUtils_GetRenderNode(nodeHandle_, &renderHandle_); | 661 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { |
| 623 | - OH_ArkUI_RenderNodeUtils_AddChild(render_->GetHandle(), renderHandle_); | 662 | + return nullptr; |
| 663 | + } | ||
| 664 | + result = OH_ArkUI_RenderNodeUtils_GetRenderNode(nodeHandle_, &renderHandle_); | ||
| 665 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 666 | + OH_ArkUI_NativeModule_RemoveAdoptedChild(custom_->GetHandle(), nodeHandle_); | ||
| 667 | + return nullptr; | ||
| 668 | + } | ||
| 669 | + result = OH_ArkUI_RenderNodeUtils_AddChild(render_->GetHandle(), renderHandle_); | ||
| 670 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 671 | + OH_ArkUI_RenderNodeUtils_DisposeNode(renderHandle_); | ||
| 672 | + renderHandle_ = nullptr; | ||
| 673 | + OH_ArkUI_NativeModule_RemoveAdoptedChild(custom_->GetHandle(), nodeHandle_); | ||
| 674 | + return nullptr; | ||
| 675 | + } | ||
| 624 | return nullptr; | 676 | return nullptr; |
| 625 | } | 677 | } |
| 626 | ``` | 678 | ``` |
| @@ -633,9 +685,15 @@ | |||
| 633 | 685 | ||
| 634 | napi_value RemoveAdopt(napi_env env, napi_callback_info info) | 686 | napi_value RemoveAdopt(napi_env env, napi_callback_info info) |
| 635 | { | 687 | { |
| 636 | - OH_ArkUI_NativeModule_RemoveAdoptedChild(custom_->GetHandle(), nodeHandle_); | 688 | + int32_t result = OH_ArkUI_NativeModule_RemoveAdoptedChild(custom_->GetHandle(), nodeHandle_); |
| 689 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 690 | + return nullptr; | ||
| 691 | + } | ||
| 637 | // 解除节点的接纳状态后,需要额外调用OH_ArkUI_RenderNodeUtils_DisposeNode释放对应的渲染节点,否则会导致内存泄漏。 | 692 | // 解除节点的接纳状态后,需要额外调用OH_ArkUI_RenderNodeUtils_DisposeNode释放对应的渲染节点,否则会导致内存泄漏。 |
| 638 | - OH_ArkUI_RenderNodeUtils_DisposeNode(renderHandle_); | 693 | + result = OH_ArkUI_RenderNodeUtils_DisposeNode(renderHandle_); |
| 694 | + if (result != ARKUI_ERROR_CODE_NO_ERROR) { | ||
| 695 | + return nullptr; | ||
| 696 | + } | ||
| 639 | nodeHandle_ = nullptr; | 697 | nodeHandle_ = nullptr; |
| 640 | renderHandle_ = nullptr; | 698 | renderHandle_ = nullptr; |
| 641 | return nullptr; | 699 | return nullptr; |
| @@ -26,10 +26,10 @@ const unsigned int VALUE_3 = 50; | |||
| 26 | { | 26 | { |
| 27 | ArkUI_NativeNodeAPI_1* nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); | 27 | ArkUI_NativeNodeAPI_1* nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); |
| 28 | ArkUI_NodeHandle testNode = nodeAPI->createNode(ARKUI_NODE_COLUMN); | 28 | ArkUI_NodeHandle testNode = nodeAPI->createNode(ARKUI_NODE_COLUMN); |
| 29 | - ArkUI_NumberValue value[] = {VALUE_1}; | 29 | + ArkUI_NumberValue value[] = {{.f32 = VALUE_1}}; |
| 30 | ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)}; | 30 | ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)}; |
| 31 | - value[0].f32 = VALUE_2; | ||
| 32 | nodeAPI->setAttribute(testNode, NODE_WIDTH, &item); | 31 | nodeAPI->setAttribute(testNode, NODE_WIDTH, &item); |
| 32 | + value[0].f32 = VALUE_2; | ||
| 33 | nodeAPI->setAttribute(testNode, NODE_HEIGHT, &item); | 33 | nodeAPI->setAttribute(testNode, NODE_HEIGHT, &item); |
| 34 | struct IdList { | 34 | struct IdList { |
| 35 | int32_t id = -1; | 35 | int32_t id = -1; |
| @@ -44,7 +44,7 @@ const unsigned int VALUE_3 = 50; | |||
| 44 | nodeAPI->setAttribute(button, NODE_HEIGHT, &item); | 44 | nodeAPI->setAttribute(button, NODE_HEIGHT, &item); |
| 45 | nodeAPI->addChild(testNode, button); | 45 | nodeAPI->addChild(testNode, button); |
| 46 | nodeAPI->registerNodeEvent(button, NODE_ON_CLICK, 1, &idl); | 46 | nodeAPI->registerNodeEvent(button, NODE_ON_CLICK, 1, &idl); |
| 47 | - OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT, "GetNodeUniqueId", "GetNodeHandleByUniqueId success1"); | 47 | + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT, "GetNodeUniqueId", "Node initialized, waiting for button click"); |
| 48 | nodeAPI->registerNodeEventReceiver([](ArkUI_NodeEvent *event) { | 48 | nodeAPI->registerNodeEventReceiver([](ArkUI_NodeEvent *event) { |
| 49 | auto targetId = OH_ArkUI_NodeEvent_GetTargetId(event); | 49 | auto targetId = OH_ArkUI_NodeEvent_GetTargetId(event); |
| 50 | if (targetId == 1) { | 50 | if (targetId == 1) { |
| @@ -56,6 +56,8 @@ const unsigned int VALUE_3 = 50; | |||
| 56 | } | 56 | } |
| 57 | } | 57 | } |
| 58 | }); | 58 | }); |
| 59 | + return std::make_shared<ArkUINode>(testNode); | ||
| 60 | + } | ||
| 59 | ``` | 61 | ``` |
| 60 | 62 | ||
| 61 | ## 通过用户id获取节点信息 | 63 | ## 通过用户id获取节点信息 |
| @@ -96,7 +98,7 @@ const unsigned int VALUE_3 = 50; | |||
| 96 | 98 | ||
| 97 | <!-- @[ndknodequeryoperate3_start](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NdkNodeQueryOperate/entry/src/main/cpp/GetNodeByIdExample.h) --> | 99 | <!-- @[ndknodequeryoperate3_start](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NdkNodeQueryOperate/entry/src/main/cpp/GetNodeByIdExample.h) --> |
| 98 | 100 | ||
| 99 | - ```c | 101 | + ```cpp |
| 100 | // GetNodeByIdExample.h | 102 | // GetNodeByIdExample.h |
| 101 | #ifndef MYAPPLICATION_GETNODEBYID_H | 103 | #ifndef MYAPPLICATION_GETNODEBYID_H |
| 102 | #define MYAPPLICATION_GETNODEBYID_H | 104 | #define MYAPPLICATION_GETNODEBYID_H |
| @@ -289,11 +291,11 @@ const unsigned int VALUE_3 = 50; | |||
| 289 | } | 291 | } |
| 290 | ``` | 292 | ``` |
| 291 | 293 | ||
| 292 | -2. 新建`MoveTo.h`文件,在其中创建Stack节点,通过OH_ArkUI_NodeUtils_MoveTo接口移动Stack节点。 | 294 | +2. 新建`MoveToExample.h`文件,在其中创建Stack节点,通过OH_ArkUI_NodeUtils_MoveTo接口移动Stack节点。 |
| 293 | 295 | ||
| 294 | <!-- @[ndknodequeryoperate5_start](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NdkNodeQueryOperate/entry/src/main/cpp/MoveToExample.h) --> | 296 | <!-- @[ndknodequeryoperate5_start](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NdkNodeQueryOperate/entry/src/main/cpp/MoveToExample.h) --> |
| 295 | 297 | ||
| 296 | - ```c | 298 | + ```cpp |
| 297 | // MoveToExample.h | 299 | // MoveToExample.h |
| 298 | #ifndef MYAPPLICATION_MOVETO_H | 300 | #ifndef MYAPPLICATION_MOVETO_H |
| 299 | #define MYAPPLICATION_MOVETO_H | 301 | #define MYAPPLICATION_MOVETO_H |
| @@ -312,7 +314,9 @@ const unsigned int VALUE_3 = 50; | |||
| 312 | ArkUI_NodeHandle node; | 314 | ArkUI_NodeHandle node; |
| 313 | ArkUI_NodeHandle targetParent; | 315 | ArkUI_NodeHandle targetParent; |
| 314 | }; | 316 | }; |
| 315 | - A* a = new A; | 317 | + // 本示例只创建一个节点树,使用静态存储保证事件回调数据在节点销毁前仍然有效。 |
| 318 | + static A eventData{}; | ||
| 319 | + A* a = &eventData; | ||
| 316 | 320 | ||
| 317 | // 创建根节点Scroll | 321 | // 创建根节点Scroll |
| 318 | ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); | 322 | ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); |
| @@ -417,7 +421,6 @@ const unsigned int VALUE_3 = 50; | |||
| 417 | if (a != nullptr) { | 421 | if (a != nullptr) { |
| 418 | OH_ArkUI_NodeUtils_MoveTo(a->node, a->targetParent, 2); | 422 | OH_ArkUI_NodeUtils_MoveTo(a->node, a->targetParent, 2); |
| 419 | nodeAPI->unregisterNodeEvent(eventNode, NODE_ON_CLICK); | 423 | nodeAPI->unregisterNodeEvent(eventNode, NODE_ON_CLICK); |
| 420 | - delete a; | ||
| 421 | } | 424 | } |
| 422 | } | 425 | } |
| 423 | }; | 426 | }; |
| @@ -556,7 +559,7 @@ const unsigned int VALUE_3 = 50; | |||
| 556 | 559 | ||
| 557 | <!-- @[ndknodequeryoperate7_start](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NdkNodeQueryOperate/entry/src/main/cpp/Attribute_util.h) --> | 560 | <!-- @[ndknodequeryoperate7_start](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/NdkNodeQueryOperate/entry/src/main/cpp/Attribute_util.h) --> |
| 558 | 561 | ||
| 559 | - ```c | 562 | + ```cpp |
| 560 | #ifndef MYAPPLICATION_ATTRIBUTE_UTIL_H | 563 | #ifndef MYAPPLICATION_ATTRIBUTE_UTIL_H |
| 561 | #define MYAPPLICATION_ATTRIBUTE_UTIL_H | 564 | #define MYAPPLICATION_ATTRIBUTE_UTIL_H |
| 562 | 565 | ||
| @@ -696,7 +699,7 @@ const unsigned int VALUE_3 = 50; | |||
| 696 | } | 699 | } |
| 697 | ``` | 700 | ``` |
| 698 | 701 | ||
| 699 | -4. 运行程序,点击按钮,切换图片正常展示。 | 702 | +4. 运行程序,点击按钮切换组件分支,图片正常展示且无闪烁。 |
| 700 | 703 | ||
| 701 |  | 704 |  |
| 702 | 705 | ||
| @@ -704,7 +707,7 @@ const unsigned int VALUE_3 = 50; | |||
| 704 | 707 | ||
| 705 | NDK支持通过不同的展开方式获取目标节点下的有效节点信息。例如,在LazyForEach场景下,可以处理存在多个子节点的情况。 | 708 | NDK支持通过不同的展开方式获取目标节点下的有效节点信息。例如,在LazyForEach场景下,可以处理存在多个子节点的情况。 |
| 706 | 709 | ||
| 707 | -从API version 20开始,使用[OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getfirstchildindexwithoutexpand)接口,可以获取目标节点的第一个存在于组件树的节点。使用[OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getlastchildindexwithoutexpand)接口,可以获取目标节点的最后一个存在于组件树的节点。[OH_ArkUI_NodeUtils_GetChildWithExpandMode](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getchildwithexpandmode)接口,可以通过不同的节点展开模式获取对应下标的子节点。 | 710 | +从API version 20开始,使用[OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getfirstchildindexwithoutexpand)接口,可以获取目标节点在不展开节点时的第一个子节点下标。使用[OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getlastchildindexwithoutexpand)接口,可以获取目标节点在不展开节点时的最后一个子节点下标。使用[OH_ArkUI_NodeUtils_GetChildWithExpandMode](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getchildwithexpandmode)接口,可以通过不同的节点展开模式获取对应下标的子节点。 |
| 708 | 711 | ||
| 709 | > **说明:** | 712 | > **说明:** |
| 710 | > | 713 | > |
| @@ -850,6 +853,7 @@ NDK支持通过不同的展开方式获取目标节点下的有效节点信息 | |||
| 850 | } | 853 | } |
| 851 | .cachedCount(5) | 854 | .cachedCount(5) |
| 852 | .listDirection(Axis.Horizontal) | 855 | .listDirection(Axis.Horizontal) |
| 856 | + .id("LazyForEachList") | ||
| 853 | } | 857 | } |
| 854 | 858 | ||
| 855 | class MyNodeController extends NodeController { | 859 | class MyNodeController extends NodeController { |
| @@ -980,14 +984,15 @@ NDK支持通过不同的展开方式获取目标节点下的有效节点信息 | |||
| 980 | 984 | ||
| 981 | ```c | 985 | ```c |
[🟢低] 代码块语言标记建议修正为 cpp 此代码块标记为 ![]() ![]() | |||
| 982 | ArkUI_NodeHandle childNode = nullptr; | 986 | ArkUI_NodeHandle childNode = nullptr; |
| 983 | - OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("N3", &childNode); | 987 | + // ArkTS侧List已设置.id("LazyForEachList")。 |
| 988 | + OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("LazyForEachList", &childNode); | ||
| 984 | 989 | ||
| 985 | uint32_t index = 0; | 990 | uint32_t index = 0; |
| 986 | OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand(childNode, &index); | 991 | OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand(childNode, &index); |
| 987 | uint32_t index1 = 0; | 992 | uint32_t index1 = 0; |
| 988 | OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand(childNode, &index1); | 993 | OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand(childNode, &index1); |
| 989 | ArkUI_NodeHandle child = nullptr; | 994 | ArkUI_NodeHandle child = nullptr; |
| 990 | - auto result = OH_ArkUI_NodeUtils_GetChildWithExpandMode(childNode, 3, &child, 0); | 995 | + auto result = OH_ArkUI_NodeUtils_GetChildWithExpandMode(childNode, 3, &child, ARKUI_LAZY_EXPAND); |
| 991 | OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager", | 996 | OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager", |
| 992 | "firstChildIndex - lastChildIndex == %{public}d -- %{public}d, -- getResult = %{public}d", | 997 | "firstChildIndex - lastChildIndex == %{public}d -- %{public}d, -- getResult = %{public}d", |
| 993 | index, index1, result); | 998 | index, index1, result); |


这个得看同源打标是什么