已合并
docs: Supplement the autolinking sample. #1570
docs: Supplement the autolinking sample. #1570
已合并
xiedairong创建于 2025年9月17日
共 72 个文件变更+1641-0
@@ -0,0 +1,12 @@
1+/node_modules
2+/oh_modules
3+/local.properties
4+/.idea
5+**/build
6+/.hvigor
7+.cxx
8+/.clangd
9+/.clang-format
10+/.clang-tidy
11+**/.test
12+/.appanalyzer
@@ -0,0 +1,10 @@
1+{
2+ "app": {
3+ "bundleName": "com.example.autolink",
4+ "vendor": "example",
5+ "versionCode": 1000000,
6+ "versionName": "1.0.0",
7+ "icon": "$media:app_icon",
8+ "label": "$string:app_name"
9+ }
10+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "app_name",
5+ "value": "harmony"
6+ }
7+ ]
8+}
@@ -0,0 +1,40 @@
1+{
2+ "app": {
3+ "signingConfigs": [],
4+ "products": [
5+ {
6+ "name": "default",
7+ "signingConfig": "default",
8+ "compatibleSdkVersion": "5.0.1(13)",
9+ "runtimeOS": "HarmonyOS",
10+ }
11+ ],
12+ "buildModeSet": [
13+ {
14+ "name": "debug",
15+ },
16+ {
17+ "name": "release"
18+ }
19+ ]
20+ },
21+ "modules": [
22+ {
23+ "name": "entry",
24+ "srcPath": "./entry",
25+ "targets": [
26+ {
27+ "name": "default",
28+ "applyToProducts": [
29+ "default"
30+ ]
31+ }
32+ ]
33+ },
34+ // If you need to recompile library.har, you can uncomment this section.
35+ // {
36+ // "name": "library",
37+ // "srcPath": "../third-party-library-sample/harmony/library",
38+ // }
39+ ]
40+}
@@ -0,0 +1,8 @@
1+/node_modules
2+/oh_modules
3+/.preview
4+/build
5+/.cxx
6+/.test
7+/src/main/cpp/generated
8+/src/main/resources/rawfile/bundle.harmony.js
@@ -0,0 +1,22 @@
1+{
2+ "apiType": "stageMode",
3+ "buildOption": {
4+ "arkOptions": {
5+ // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
6+ },
7+ "externalNativeOptions": {
8+ "path": "./src/main/cpp/CMakeLists.txt",
9+ "arguments": "",
10+ "cppFlags": "",
11+ }
12+ 
13+ },
14+ "targets": [
15+ {
16+ "name": "default"
17+ },
18+ {
19+ "name": "ohosTest",
20+ }
21+ ]
22+}
@@ -0,0 +1,20 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
9+import { createRNOHModulePlugin } from "@rnoh/hvigor-plugin"
10+ 
11+export default {
12+ system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
13+ plugins: [
14+ createRNOHModulePlugin({
15+ nodeModulesPath: "../ReactProject/node_modules",
16+ codegen: null,
17+ autolinking: {}
18+ }),
19+ ]
20+}
@@ -0,0 +1,10 @@
1+{
2+ "license": "",
3+ "devDependencies": {},
4+ "author": "",
5+ "name": "entry",
6+ "description": "Please describe the basic information.",
7+ "main": "",
8+ "version": "1.0.0",
9+ "dependencies": {}
10+}
@@ -0,0 +1,39 @@
1+# Copyright (c) 2025 Huawei Technologies Co., Ltd.
2+#
3+# This source code is licensed under the MIT license found in the
4+# LICENSE-MIT file in the root directory of this source tree.
5+ 
6+project(rnapp)
7+cmake_minimum_required(VERSION 3.4.1)
8+set(CMAKE_SKIP_BUILD_RPATH TRUE)
9+set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
10+set(OH_MODULES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../oh_modules")
11+set(RNOH_CPP_DIR "${OH_MODULES_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
12+file(REAL_PATH "${RNOH_CPP_DIR}" RNOH_CPP_DIR)
13+set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
14+set(LOG_VERBOSITY_LEVEL 1)
15+set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
16+set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -fPIE -pie")
17+set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
18+add_compile_definitions(WITH_HITRACE_SYSTRACE)
19+ 
20+include("${CMAKE_CURRENT_SOURCE_DIR}/autolinking.cmake")
21+add_subdirectory("${RNOH_CPP_DIR}" ./rn)
22+ 
23+# RNOH_BEGIN: manual_package_linking_1
24+# RNOH_END: manual_package_linking_1
25+ 
26+file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
27+ 
28+add_library(rnoh_app SHARED
29+ ${GENERATED_CPP_FILES}
30+ "./PackageProvider.cpp"
31+ "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
32+)
33+ 
34+target_link_libraries(rnoh_app PUBLIC rnoh)
35+ 
36+autolink_libraries(rnoh_app)
37+ 
38+# RNOH_BEGIN: manual_package_linking_2
39+# RNOH_END: manual_package_linking_2
@@ -0,0 +1,17 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+#include "RNOH/PackageProvider.h"
9+#include "RNOHPackagesFactory.h"
10+ 
11+using namespace rnoh;
12+ 
13+std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(
14+ Package::Context ctx) {
15+ auto packages = createRNOHPackages(ctx); // <= autolinking
16+ return packages;
17+}
@@ -0,0 +1,14 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ */
7+import type { RNPackageContext, RNOHPackage } from '@rnoh/react-native-openharmony';
8+import { createRNOHPackages as createRNOHPackagesAutolinking } from "./RNOHPackagesFactory"
9+ 
10+export function getRNOHPackages(ctx: RNPackageContext): RNOHPackage[] {
11+ return [
12+ ...createRNOHPackagesAutolinking(ctx)
13+ ]
14+}
@@ -0,0 +1,18 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { RNAbility } from '@rnoh/react-native-openharmony';
9+ 
10+export default class EntryAbility extends RNAbility {
11+ getPagePath() {
12+ return 'pages/Index';
13+ }
14+ 
15+ override getRNOHWorkerScriptUrl() {
16+ return "entry/ets/workers/RNOHWorker.ets"
17+ }
18+}
@@ -0,0 +1,79 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import {
9+ AnyJSBundleProvider,
10+ ComponentBuilderContext,
11+ FileJSBundleProvider,
12+ MetroJSBundleProvider,
13+ ResourceJSBundleProvider,
14+ RNApp,
15+ RNOHErrorDialog,
16+ RNOHLogger,
17+ TraceJSBundleProviderDecorator,
18+ RNOHCoreContext
19+} from '@rnoh/react-native-openharmony';
20+import { getRNOHPackages } from '../PackageProvider';
21+ 
22+@Builder
23+export function buildCustomRNComponent(ctx: ComponentBuilderContext) {}
24+ 
25+const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent);
26+ 
27+@Entry
28+@Component
29+struct Index {
30+ @StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined;
31+ @State shouldShow: boolean = false;
32+ private logger!: RNOHLogger;
33+ 
34+ aboutToAppear() {
35+ this.logger = this.rnohCoreContext!.logger.clone("Index");
36+ const stopTracing = this.logger.clone("aboutToAppear").startTracing();
37+ this.shouldShow = true;
38+ stopTracing();
39+ }
40+ 
41+ onBackPress(): boolean | undefined {
42+ // NOTE: this is required since `Ability`'s `onBackPressed` function always
43+ // terminates or puts the app in the background, but we want Ark to ignore it completely
44+ // when handled by RN
45+ this.rnohCoreContext!.dispatchBackPress();
46+ return true;
47+ }
48+ 
49+ build() {
50+ Column() {
51+ if (this.rnohCoreContext && this.shouldShow) {
52+ if (this.rnohCoreContext?.isDebugModeEnabled) {
53+ RNOHErrorDialog({ ctx: this.rnohCoreContext });
54+ }
55+ RNApp({
56+ rnInstanceConfig: {
57+ createRNPackages: getRNOHPackages,
58+ },
59+ initialProps: { "foo": "bar" } as Record<string, string>,
60+ appKey: "app_name",
61+ wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
62+ jsBundleProvider: new TraceJSBundleProviderDecorator(
63+ new AnyJSBundleProvider([
64+ new MetroJSBundleProvider(),
65+ // NOTE: to load the bundle from file, place it in
66+ // `/data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js`
67+ // on your device. The path mismatch is due to app sandboxing on OpenHarmony
68+ new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
69+ new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
70+ new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
71+ ]),
72+ this.rnohCoreContext.logger),
73+ })
74+ }
75+ }
76+ .height('100%')
77+ .width('100%')
78+ }
79+}
@@ -0,0 +1,23 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ */
7+
8+ /**
9+ * Importing some ets files in the following way results in runtime errors e.g.
10+ * import { setupRNOHWorker } from "@rnoh/react-native-openharmony"
11+ *
12+ * It looks like this is ArkTS problem and it may be fixed in the future.
13+ * For the time being, use direct imports.
14+ */
15+import { setupRNOHWorker } from "@rnoh/react-native-openharmony/src/main/ets/setupRNOHWorker"
16+import { getRNOHPackages } from "../PackageProvider"
17+ 
18+setupRNOHWorker({
19+ createWorkerRNInstanceConfig: (_rnInstanceName) => {
20+ return { thirdPartyPackagesFactory: getRNOHPackages }
21+ }
22+})
23+ 
@@ -0,0 +1,50 @@
1+{
2+ "module": {
3+ "name": "entry",
4+ "type": "entry",
5+ "description": "$string:module_desc",
6+ "mainElement": "EntryAbility",
7+ "deviceTypes": [
8+ "phone",
9+ "tablet",
10+ "2in1"
11+ ],
12+ "metadata": [
13+ {
14+ "name": "half_leading",
15+ "value": "true"
16+ },
17+ {
18+ "name": "can_preview_text",
19+ "value": "true",
20+ }
21+ ],
22+ "libIsolation": false,
23+ "deliveryWithInstall": true,
24+ "installationFree": false,
25+ "pages": "$profile:main_pages",
26+ "abilities": [
27+ {
28+ "name": "EntryAbility",
29+ "srcEntry": "./ets/entryability/EntryAbility.ets",
30+ "description": "$string:EntryAbility_desc",
31+ "icon": "$media:layered_image",
32+ "label": "$string:EntryAbility_label",
33+ // "orientation": "auto_rotation",
34+ "startWindowIcon": "$media:startIcon",
35+ "startWindowBackground": "$color:start_window_background",
36+ "exported": true,
37+ "skills": [
38+ {
39+ "entities": [
40+ "entity.system.home"
41+ ],
42+ "actions": [
43+ "action.system.home"
44+ ]
45+ }
46+ ]
47+ },
48+ ]
49+ }
50+}
@@ -0,0 +1,8 @@
1+{
2+ "color": [
3+ {
4+ "name": "start_window_background",
5+ "value": "#FFFFFF"
6+ }
7+ ]
8+}
@@ -0,0 +1,16 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "module description"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "AutolinkingSample"
14+ }
15+ ]
16+}
@@ -0,0 +1,7 @@
1+{
2+ "layered-image":
3+ {
4+ "background" : "$media:background",
5+ "foreground" : "$media:foreground"
6+ }
7+}
@@ -0,0 +1,5 @@
1+{
2+ "src": [
3+ "pages/Index"
4+ ]
5+}
@@ -0,0 +1,16 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "module description"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "AutolinkingSample"
14+ }
15+ ]
16+}
@@ -0,0 +1,16 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "模块描述"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "FabricComponentSample"
14+ }
15+ ]
16+}
@@ -0,0 +1,22 @@
1+{
2+ "modelVersion": "5.0.0",
3+ "dependencies": {
4+ "@rnoh/hvigor-plugin": "file:../../ReactProject/node_modules/@react-native-oh/react-native-harmony-cli/harmony/rnoh-hvigor-plugin-0.77.22.tgz"
5+ },
6+ "execution": {
7+ // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */
8+ // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
9+ // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
10+ // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
11+ // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
12+ },
13+ "logging": {
14+ // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
15+ },
16+ "debugging": {
17+ // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
18+ },
19+ "nodeOptions": {
20+ // "maxOldSpaceSize": 4096 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process */
21+ }
22+}
@@ -0,0 +1,23 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { appTasks } from '@ohos/hvigor-ohos-plugin';
9+import { createRNOHProjectPlugin } from '@rnoh/hvigor-plugin';
10+ 
11+export default {
12+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
13+ plugins: [
14+ createRNOHProjectPlugin({
15+ nodeModulesPath: "../ReactProject/node_modules",
16+ bundler: {
17+ config: "../ReactProject/metro.config.js",
18+ bundleOutput: "./entry/src/main/resources/rawfile/bundle.harmony.js",
19+ assetsDest: "./entry/src/main/resources/rawfile/assets",
20+ }
21+ })
22+ ]
23+}
@@ -0,0 +1,19 @@
1+{
2+ modelVersion: "5.0.0",
3+ name: "harmony",
4+ version: "1.0.0",
5+ description: "Please describe the basic information.",
6+ main: "",
7+ author: "",
8+ license: "",
9+ dependencies: {
10+ "@rnoh/react-native-openharmony": "file:../ReactProject/node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har",
11+ },
12+ devDependencies: {
13+ "@ohos/hypium": "1.0.16",
14+ "@ohos/hamock": "1.0.0",
15+ },
16+ overrides: {
17+ "@rnoh/react-native-openharmony": "file:../ReactProject/node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har",
18+ },
19+}
@@ -0,0 +1,28 @@
1+# Description
2+This is the demo project for RNOH Autolinking.
3+ 
4+ 
5+# Directory Structure
6+ 
7+```md
8+AutolinkingSample
9+├── NativeProject harmony工程
10+├── react-native-oh RNOH前端及手脚架
11+├── ReactProject 前端工程
12+├── screenshots 效果图
13+├── third-party-library-sample RN三方件示例
14+└── README.md
15+```
16+ 
17+ 
18+# Environment Setup
19+ 
20+1. Save `react-native-oh-react-native-harmony-0.77.xx.tgz` and `react-native-oh-react-native-harmony-cli-0.77.xx.tgz` to the `react-native-oh` folder.
21+1. Run **npm pack** in `third-party-library-sample` to generate a .tgz file.
22+1. Modify the `@react-native-oh/react-native-harmony` and `@react-native-oh/react-native-harmony-cli` dependency path of `ReactProject/package.json`.
23+1. Modify the `@rnoh/hvigor-plugin` dependency path of `NativeProject/hvigor/hvigor-config.json5`;
24+1. Run **npm i** in `ReactProject` to install the dependency.
25+1. Open `NativeProject` in DevEco Studio and run **Sync and Refresh Project**.
26+1. Click File > Project Structure > Signing Configs, log in and complete the signing.
27+1. Run **npm start** in `ReactProject` to start Metro.
28+1. Click **run** in the upper right corner to start the project.
@@ -0,0 +1,46 @@
1+# 说明
2+ 
3+这是RNOH Autolinking的demo工程
4+ 
5+## 目录结构
6+ 
7+```md
8+AutolinkingSample
9+├── NativeProject harmony工程
10+├── react-native-oh RNOH前端及手脚架
11+├── ReactProject 前端工程
12+├── screenshots 效果图
13+├── third-party-library-sample RN三方件示例
14+└── README.md
15+```
16+ 
17+## 环境搭建
18+ 
19+1. 将 `react-native-oh-react-native-harmony-0.77.xx.tgz` 和 `react-native-oh-react-native-harmony-cli-0.77.xx.tgz` 放置在 `react-native-oh` 目录下;
20+1. 在 `third-party-library-sample` 中运行 **npm pack** 进行打包;
21+1. 修改 `ReactProject/package.json` 中 `@react-native-oh/react-native-harmony` 和 `@react-native-oh/react-native-harmony-cli` 的版本号;
22+1. 修改 `NativeProject/hvigor/hvigor-config.json5` 中 `@rnoh/hvigor-plugin` 的版本号;
23+1. 在 `ReactProject` 目录下执行 **npm i** 安装依赖;
24+1. 用 DevEco Studio 打开 `NativeProject`,执行 **Sync and Refresh Project**;
25+1. 点击 File > Project Structure > Signing Configs,登录并完成签名;
26+1. 在 `ReactProject` 目录下执行 **npm start** 启动Metro;
27+1. 点击 DevEco Studio 右上角的 **run** 启动项目;
28+ 
29+> NOTE: 安装时项目默认使用最新的RNOH依赖,如需安装特定版本请查阅[RNOH版本演进规划和分支策略](https://gitcode.com/openharmony-sig/ohos_react_native/wiki/RNOH版本演进规划和分支策略.md)获取版本号。
30+>
31+> npm依赖安装: `npm i @react-native-oh/react-native-harmony@0.77.xx` `npm i --save-dev @react-native-oh/react-native-harmony-cli@0.77.xx`
32+>
33+> ohpm依赖安装:`ohpm i @rnoh/react-native-openharmony@0.77.xx`
34+ 
35+## 效果预览
36+ 
37+启动后页面效果如下:
38+ 
39+![FabricComponentSample运行界面](./screenshots/Screenshot.jpg)
40+ 
41+1. 点击【点击滚到顶】按钮,弹窗会滚到顶部;
42+1. 弹窗会滚到顶部后向下拉,会根据滚动位置决定最终停留在中间还是回到顶部;
43+1. 点击【选项1/2/3】,选项右侧会显示选中状态;
44+1. 点击【确定】按钮,会在VSCode控制台打印对应选项的value,如:`{"target": 14, "value": [1]}`;
45+1. 点击【现在是单选】按钮,按钮文案会改成【现在是多选】,继续点击【选项1/2/3】,可以让多个选项切换到选中状态;
46+1. 点击【确定】按钮,会在VSCode控制台打印对应选项的value,如:`{"target": 14, "value": [1,2]}`;
@@ -0,0 +1,11 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+module.exports = {
9+ root: true,
10+ extends: '@react-native',
11+};
@@ -0,0 +1,66 @@
1+# OSX
2+#
3+.DS_Store
4+ 
5+# Xcode
6+#
7+build/
8+*.pbxuser
9+!default.pbxuser
10+*.mode1v3
11+!default.mode1v3
12+*.mode2v3
13+!default.mode2v3
14+*.perspectivev3
15+!default.perspectivev3
16+xcuserdata
17+*.xccheckout
18+*.moved-aside
19+DerivedData
20+*.hmap
21+*.ipa
22+*.xcuserstate
23+ios/.xcode.env.local
24+ 
25+# Android/IntelliJ
26+#
27+build/
28+.idea
29+.gradle
30+local.properties
31+*.iml
32+*.hprof
33+.cxx/
34+*.keystore
35+!debug.keystore
36+ 
37+# node.js
38+#
39+node_modules/
40+npm-debug.log
41+yarn-error.log
42+ 
43+# fastlane
44+#
45+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
46+# screenshots whenever they are needed.
47+# For more information about the recommended setup visit:
48+# https://docs.fastlane.tools/best-practices/source-control/
49+ 
50+**/fastlane/report.xml
51+**/fastlane/Preview.html
52+**/fastlane/screenshots
53+**/fastlane/test_output
54+ 
55+# Bundle artifact
56+*.jsbundle
57+ 
58+# Ruby / CocoaPods
59+/ios/Pods/
60+/vendor/bundle/
61+ 
62+# Temporary files created by Metro to check the health of the file watcher
63+.metro-health-check*
64+ 
65+# testing
66+/coverage
@@ -0,0 +1,14 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+module.exports = {
9+ arrowParens: 'avoid',
10+ bracketSameLine: true,
11+ bracketSpacing: false,
12+ singleQuote: true,
13+ trailingComma: 'all',
14+};
@@ -0,0 +1,72 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+ 
9+import React, { useEffect, useRef, useState } from 'react';
10+import { StyleSheet, Text, View, Pressable, UIManager, findNodeHandle, Dimensions } from 'react-native';
11+import { QDGestureFloat, SampleAnyThreadTurboModule, SampleTurboModule } from 'third-party-library';
12+import SelectBoxApp from './src/SelectBoxApp'
13+ 
14+const ScreenHeight = Dimensions.get('window').height;
15+ 
16+const App = () => {
17+ const floatRef = useRef(null)
18+ const [addResult, setAddResult] = useState(0)
19+ const [powerResult, setPowerResult] = useState(0)
20+ useEffect(() => {
21+ const result1 = SampleTurboModule.add(2, 4);
22+ setAddResult(result1);
23+ SampleAnyThreadTurboModule.power(2, 4).then(result2 => {
24+ setPowerResult(result2);
25+ });
26+ }, [])
27+ return (
28+ <View style={styles.container}>
29+ <QDGestureFloat
30+ ref={floatRef}
31+ style={styles.container}
32+ stopPercent={0.5}
33+ stopPercentMax={0.75}
34+ onScroll={(event) => {
35+ console.log(event.nativeEvent.offsetY)
36+ }}
37+ >
38+ <View style={{ width: '100%', height: 1000, backgroundColor: 'yellow' }}>
39+ <Pressable onPress={() => {
40+ if (floatRef.current) {
41+ // RN向原生发送消息
42+ UIManager.dispatchViewManagerCommand(
43+ findNodeHandle(floatRef.current),
44+ 'scrollTo',
45+ [ScreenHeight, true]
46+ );
47+ }
48+ }}>
49+ <Text style={{ fontSize: 50, color: 'red' }}>点击滚到顶</Text>
50+ </Pressable>
51+ <Text style={{ backgroundColor: 'white' }}>2 + 4 = {addResult}, 2 ^ 4 = {powerResult}</Text>
52+ <SelectBoxApp />
53+ </View>
54+ </QDGestureFloat>
55+ </View>
56+ )
57+}
58+ 
59+const styles = StyleSheet.create({
60+ container: {
61+ flex: 1,
62+ },
63+ title: {
64+ backgroundColor: 'green',
65+ color: 'white',
66+ fontSize: 20,
67+ padding: 8,
68+ textAlign: 'center'
69+ }
70+});
71+ 
72+export default App;
@@ -0,0 +1,9 @@
1+source 'https://rubygems.org'
2+ 
3+# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4+ruby ">= 2.6.10"
5+ 
6+# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper
7+# bound in the template on Cocoapods with next React Native release.
8+gem 'cocoapods', '>= 1.13', '< 1.15'
9+gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
@@ -0,0 +1,4 @@
1+{
2+ "name": "app_name",
3+ "displayName": "tester"
4+}
@@ -0,0 +1,10 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+module.exports = {
9+ presets: ['module:@react-native/babel-preset'],
10+};
@@ -0,0 +1,14 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ *
7+ * @format
8+ */
9+ 
10+import { AppRegistry } from 'react-native';
11+import { name as appName } from './app.json';
12+import App from './App';
13+ 
14+AppRegistry.registerComponent(appName, () => App);
@@ -0,0 +1,10 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+module.exports = {
9+ preset: 'react-native',
10+};
@@ -0,0 +1,29 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+const { mergeConfig, getDefaultConfig } = require('@react-native/metro-config');
9+const { createHarmonyMetroConfig } = require('@react-native-oh/react-native-harmony/metro.config');
10+ 
11+/**
12+ * @type {import("metro-config").ConfigT}
13+ */
14+const config = {
15+ // 清理metro缓存
16+ // resetCache: true,
17+ transformer: {
18+ getTransformOptions: async () => ({
19+ transform: {
20+ experimentalImportSupport: false,
21+ inlineRequires: true,
22+ },
23+ }),
24+ },
25+};
26+ 
27+module.exports = mergeConfig(getDefaultConfig(__dirname), createHarmonyMetroConfig({
28+ reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
29+}), config);
@@ -0,0 +1,39 @@
1+{
2+ "name": "ReactProject",
3+ "version": "0.0.1",
4+ "private": true,
5+ "scripts": {
6+ "lint": "eslint .",
7+ "start": "react-native start --reset-cache --client-logs",
8+ "dev": "react-native bundle-harmony --dev false --bundle-output ../NativeProject/entry/src/main/resources/rawfile/bundle.harmony.js"
9+ },
10+ "dependencies": {
11+ "react": "18.3.1",
12+ "react-native": "0.77.1",
13+ "@react-native-oh/react-native-harmony": "file:../react-native-oh/react-native-oh-react-native-harmony-0.77.22.tgz",
14+ "react-native-performance": "^5.1.2",
15+ "third-party-library": "file:../third-party-library-sample/third-party-library-sample-1.0.0.tgz"
16+ },
17+ "devDependencies": {
18+ "@react-native-community/cli": "15.0.1",
19+ "@react-native-oh/react-native-harmony-cli": "file:../react-native-oh/react-native-oh-react-native-harmony-cli-0.77.22.tgz",
20+ "@react-native/babel-preset": "0.77.1",
21+ "@babel/core": "^7.20.0",
22+ "@babel/preset-env": "^7.20.0",
23+ "@babel/runtime": "^7.20.0",
24+ "@react-native/eslint-config": "0.77.1",
25+ "@react-native/typescript-config": "0.77.1",
26+ "@react-native/metro-config": "0.77.1",
27+ "@types/react": "^18.0.24",
28+ "@types/react-test-renderer": "^18.0.0",
29+ "babel-jest": "^29.2.1",
30+ "eslint": "^8.19.0",
31+ "jest": "^29.2.1",
32+ "prettier": "^2.4.1",
33+ "react-test-renderer": "18.2.0",
34+ "typescript": "5.0.4"
35+ },
36+ "engines": {
37+ "node": ">=16"
38+ }
39+}
@@ -0,0 +1,86 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+ 
9+import React, { useRef, useState } from 'react';
10+import {
11+ Text,
12+ StyleSheet,
13+ Pressable,
14+ UIManager,
15+ findNodeHandle,
16+ View,
17+} from 'react-native';
18+import { SelectBox } from 'third-party-library';
19+ 
20+const DATA = [
21+ {
22+ id: 0,
23+ text: '选项1',
24+ },
25+ {
26+ id: 1,
27+ text: '选项2',
28+ },
29+ {
30+ id: 2,
31+ text: '选项3',
32+ },
33+];
34+ 
35+const App = () => {
36+ const [value, setValue] = useState([])
37+ const [mutil, setMutil] = useState(false)
38+ const boxRef = useRef(null)
39+ 
40+ return (
41+ <View style={styles.container}>
42+ <Pressable onPress={() => {
43+ if (boxRef.current) {
44+ // RN向原生发送消息
45+ UIManager.dispatchViewManagerCommand(
46+ findNodeHandle(boxRef.current),
47+ 'emitNativeEvent',
48+ [!mutil]
49+ );
50+ setMutil(!mutil)
51+ }
52+ }}>
53+ <Text style={styles.title}>{`现在是${mutil ? '多选' : '单选'}`}</Text>
54+ </Pressable>
55+ <SelectBox
56+ ref={boxRef}
57+ style={{
58+ flex: 1
59+ }}
60+ value={value}
61+ data={DATA}
62+ onChange={(event: any) => {
63+ setValue(event.nativeEvent.value)
64+ console.log(event.nativeEvent)
65+ }}
66+ />
67+ </View>
68+ );
69+};
70+ 
71+const styles = StyleSheet.create({
72+ container: {
73+ backgroundColor: 'white',
74+ height: 250,
75+ justifyContent: 'center',
76+ },
77+ title: {
78+ backgroundColor: 'green',
79+ color: 'white',
80+ fontSize: 20,
81+ padding: 8,
82+ textAlign: 'center'
83+ }
84+});
85+ 
86+export default App;
@@ -0,0 +1,3 @@
1+{
2+ "extends": "@react-native/typescript-config/tsconfig.json"
3+}
@@ -0,0 +1,4 @@
1+*.tgz
2+node_modules
3+harmony/library/src/main/cpp/generated
4+harmony/library/src/main/ets/generated
@@ -0,0 +1,6 @@
1+/node_modules
2+/oh_modules
3+/.preview
4+/build
5+/.cxx
6+/.test
@@ -0,0 +1,17 @@
1+/**
2+ * Use these variables when you tailor your ArkTS code. They must be of the const type.
3+ */
4+export const HAR_VERSION = '1.0.0';
5+export const BUILD_MODE_NAME = 'debug';
6+export const DEBUG = true;
7+export const TARGET_NAME = 'default';
8+ 
9+/**
10+ * BuildProfile Class is used only for compatibility purposes.
11+ */
12+export default class BuildProfile {
13+ static readonly HAR_VERSION = HAR_VERSION;
14+ static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
15+ static readonly DEBUG = DEBUG;
16+ static readonly TARGET_NAME = TARGET_NAME;
17+}
@@ -0,0 +1,8 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ */
7+ 
8+export { ThirdPartyLibrarySamplePackage as default } from './src/main/ets/ThirdPartyLibrarySamplePackage';
@@ -0,0 +1,11 @@
1+{
2+ "apiType": "stageMode",
3+ "targets": [
4+ {
5+ "name": "default"
6+ },
7+ {
8+ "name": "ohosTest"
9+ }
10+ ]
11+}
@@ -0,0 +1,13 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { harTasks } from '@ohos/hvigor-ohos-plugin';
9+ 
10+export default {
11+ system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
12+ plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
13+}
@@ -0,0 +1,11 @@
1+{
2+ "name": "@rnoh/third-party-library-sample",
3+ "version": "1.0.0",
4+ "description": "Please describe the basic information.",
5+ "main": "Index.ets",
6+ "author": "",
7+ "license": "Apache-2.0",
8+ "dependencies": {
9+ "@rnoh/react-native-openharmony": "../ReactProject/node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har"
10+ }
11+}
@@ -0,0 +1,18 @@
1+# Copyright (c) 2025 Huawei Technologies Co., Ltd.
2+#
3+# This source code is licensed under the MIT license found in the
4+# LICENSE-MIT file in the root directory of this source tree.
5+ 
6+cmake_minimum_required(VERSION 3.4.1)
7+set(CMAKE_VERBOSE_MAKEFILE on)
8+ 
9+set(third_party_library_sample_generated_dir "${CMAKE_CURRENT_SOURCE_DIR}/generated")
10+file(GLOB_RECURSE third_party_library_sample_generated_SRC "${third_party_library_sample_generated_dir}/**/*.cpp")
11+ 
12+file(GLOB third_party_library_sample_SRC CONFIGURE_DEPENDS *.cpp)
13+ 
14+add_library(third_party_library SHARED ${third_party_library_sample_SRC} ${third_party_library_sample_generated_SRC})
15+target_include_directories(third_party_library PUBLIC
16+ ${CMAKE_CURRENT_SOURCE_DIR}
17+ ${third_party_library_sample_generated_dir})
18+target_link_libraries(third_party_library PUBLIC rnoh)
@@ -0,0 +1,91 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+#pragma once
9+ 
10+#include "QDGestureFloatComponentInstance.h"
11+ 
12+namespace rnoh {
13+ 
14+QDGestureFloatComponentInstance::QDGestureFloatComponentInstance(
15+ Context context)
16+ : Super(std::move(context)) {
17+ m_scrollNode.setScrollNodeDelegate(this);
18+ getLocalRootArkUINode().insertChild(m_scrollNode, 0);
19+ m_scrollNode.insertChild(m_scrollContentNode);
20+ m_scrollContentNode.insertChild(m_transparentNode, 0);
21+ m_scrollContentNode.insertChild(m_contentNode, 1);
22+ 
23+ m_scrollNode.setScrollBarDisplayMode(
24+ ArkUI_ScrollBarDisplayMode::ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF);
25+}
26+ 
27+facebook::react::Point rnoh::QDGestureFloatComponentInstance::getCurrentOffset()
28+ const {
29+ auto offset = m_scrollNode.getScrollOffset();
30+ offset.y -= m_height;
31+ return offset;
32+}
33+ 
34+StackNode& QDGestureFloatComponentInstance::getLocalRootArkUINode() {
35+ return m_scrollContainerNode;
36+}
37+ 
38+void QDGestureFloatComponentInstance::onPropsChanged(
39+ SharedConcreteProps const& props) {
40+ if (props->stopPercent) {
41+ m_stopPercent = props->stopPercent;
42+ }
43+ if (props->stopPercentMax) {
44+ m_stopPercentMax = props->stopPercentMax;
45+ }
46+};
47+ 
48+void QDGestureFloatComponentInstance::onChildInserted(
49+ ComponentInstance::Shared const& childComponentInstance,
50+ std::size_t index) {
51+ CppComponentInstance::onChildInserted(childComponentInstance, index);
52+ m_contentNode.insertChild(
53+ childComponentInstance->getLocalRootArkUINode(), index);
54+ if (!m_isInit) {
55+ auto layoutMetrics = getLayoutMetrics();
56+ m_width = layoutMetrics.frame.size.width;
57+ m_height = layoutMetrics.frame.size.height;
58+ m_transparentNode.setSize(facebook::react::Size{m_width, m_height});
59+ m_scrollNode.scrollTo(0, m_height * m_stopPercent, false);
60+ }
61+}
62+ 
63+void QDGestureFloatComponentInstance::onChildRemoved(
64+ ComponentInstance::Shared const& childComponentInstance) {
65+ CppComponentInstance::onChildRemoved(childComponentInstance);
66+ m_contentNode.removeChild(childComponentInstance->getLocalRootArkUINode());
67+}
68+ 
69+void QDGestureFloatComponentInstance::onScroll() {
70+ auto offsetY = m_scrollNode.getScrollOffset().y;
71+ facebook::react::QDGestureFloatEventEmitter::OnScroll event = {offsetY};
72+ m_eventEmitter->onScroll(event);
73+}
74+ 
75+void QDGestureFloatComponentInstance::onScrollStop() {
76+ auto y = m_scrollNode.getScrollOffset().y;
77+ if (y <= m_height && y > m_height * m_stopPercentMax) {
78+ m_scrollNode.scrollTo(0, m_height, true);
79+ } else if (
80+ y < m_height * m_stopPercent ||
81+ (y <= m_height && y > m_height * m_stopPercent)) {
82+ m_scrollNode.scrollTo(0, m_height * m_stopPercent, true);
83+ }
84+}
85+ 
86+void QDGestureFloatComponentInstance::onScrollToCommand(
87+ float offsetY,
88+ bool animated) {
89+ m_scrollNode.scrollTo(0, offsetY, animated);
90+}
91+} // namespace rnoh
@@ -0,0 +1,60 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+#pragma once
9+ 
10+#include "RNOH/arkui/ColumnNode.h"
11+#include "RNOH/arkui/ScrollNode.h"
12+#include "RNOH/arkui/StackNode.h"
13+#include "RNOH/generated/components/BaseQDGestureFloatComponentInstance.h"
14+ 
15+namespace rnoh {
16+class QDGestureFloatComponentInstance
17+ : public BaseQDGestureFloatComponentInstance,
18+ ScrollNodeDelegate {
19+ using Super = BaseQDGestureFloatComponentInstance;
20+ 
21+ private:
22+ // 根容器
23+ StackNode m_scrollContainerNode;
24+ // scroll 组件
25+ ScrollNode m_scrollNode;
26+ // scroll 内容
27+ ColumnNode m_scrollContentNode;
28+ // 上方透明区域
29+ StackNode m_transparentNode;
30+ // 内容区域
31+ StackNode m_contentNode;
32+ 
33+ // 组件宽高
34+ float m_width;
35+ float m_height;
36+ bool m_isInit = false;
37+ // 停止百分比、吸顶百分比
38+ float m_stopPercent = 0.75f;
39+ float m_stopPercentMax = 0.85f;
40+ 
41+ public:
42+ explicit QDGestureFloatComponentInstance(Context context);
43+ 
44+ StackNode& getLocalRootArkUINode();
45+ 
46+ void onPropsChanged(SharedConcreteProps const& props);
47+ 
48+ void onChildInserted(
49+ ComponentInstance::Shared const& childComponentInstance,
50+ std::size_t index) override;
51+ void onChildRemoved(
52+ ComponentInstance::Shared const& childComponentInstance) override;
53+ 
54+ facebook::react::Point getCurrentOffset() const override;
55+ 
56+ void onScroll() override;
57+ void onScrollStop() override;
58+ void onScrollToCommand(float offsetY, bool animated) override;
59+};
60+} // namespace rnoh
@@ -0,0 +1,25 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+#include "ThirdPartyLibrarySamplePackage.h"
9+#include "QDGestureFloatComponentInstance.h"
10+#include "RNOH/RNInstanceCAPI.h"
11+#include "RNOH/generated/BaseThirdPartyLibrarySamplePackage.h"
12+#include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h"
13+#include "RNOHCorePackage/ComponentBinders/ViewComponentNapiBinder.h"
14+ 
15+using namespace facebook;
16+ 
17+namespace rnoh {
18+ComponentInstance::Shared ThirdPartyLibrarySamplePackage::createComponentInstance(
19+ const ComponentInstance::Context& ctx) {
20+ if (ctx.componentName == "QDGestureFloat") {
21+ return std::make_shared<QDGestureFloatComponentInstance>(std::move(ctx));
22+ }
23+ return nullptr;
24+};
25+} // namespace rnoh
@@ -0,0 +1,25 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+#pragma once
9+ 
10+#include "RNOH/Package.h"
11+#include "RNOH/generated/BaseThirdPartyLibrarySamplePackage.h"
12+ 
13+namespace rnoh {
14+class ThirdPartyLibrarySamplePackage
15+ : public BaseThirdPartyLibrarySamplePackage {
16+ using Super = BaseThirdPartyLibrarySamplePackage;
17+ 
18+ public:
19+ explicit ThirdPartyLibrarySamplePackage(Package::Context ctx)
20+ : BaseThirdPartyLibrarySamplePackage(ctx) {}
21+ 
22+ ComponentInstance::Shared createComponentInstance(
23+ const ComponentInstance::Context& ctx) override;
24+};
25+} // namespace rnoh
@@ -0,0 +1,20 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { AnyThreadTurboModule } from "@rnoh/react-native-openharmony";
mao
maomao2025年9月23日

补充copyright

likedislike
9+import { TM } from "./generated/ts"
10+ 
11+export class SampleAnyThreadTurboModule extends AnyThreadTurboModule implements TM.SampleAnyThreadTurboModule.Spec {
12+ public static readonly NAME = TM.SampleAnyThreadTurboModule.NAME;
13+ 
14+ power(a: number, b: number): Promise<number> {
15+ this.ctx.logger.info("AutoLinkSample.AnyThreadTurboModule.power");
16+ return new Promise(resolve => {
17+ setTimeout(() => resolve(Math.pow(a, b)), 3000)
18+ })
19+ }
20+}
@@ -0,0 +1,18 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { EtsUITurboModule } from '@rnoh/react-native-openharmony/ets';
mao
maomao2025年9月23日

补充copyright

likedislike
9+import { TM } from "./generated/ts"
10+ 
11+export class SampleTurboModule extends EtsUITurboModule implements TM.SampleTurboModule.Spec {
12+ public static readonly NAME = TM.SampleTurboModule.NAME;
13+ 
14+ add(a: number, b: number): number {
15+ this.ctx.logger.info("AutoLinkSample.SampleTurboModule.add");
16+ return a + b;
17+ }
18+}
@@ -0,0 +1,132 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { RNOHContext, RNViewBase } from '@rnoh/react-native-openharmony';
9+import { RNC } from "./generated";
10+ 
11+let nextKey = 0;
12+ 
13+@Observed
14+class SelectItems {
15+ key: number;
16+ id: number;
17+ text: string;
18+ selected: boolean;
19+ 
20+ constructor(id: number, text: string, selected: boolean) {
21+ this.key = nextKey++;
22+ this.id = id;
23+ this.text = text;
24+ this.selected = selected;
25+ }
26+}
27+ 
28+@Component
29+export struct SelectBox {
30+ public static readonly NAME = RNC.SelectBox.NAME;
31+ public ctx!: RNOHContext;
32+ public tag: number = 0;
33+ @State descriptorWrapper: RNC.SelectBox.DescriptorWrapper = {} as RNC.SelectBox.DescriptorWrapper;
34+ @State data: SelectItems[] = [];
35+ @State mutil: boolean = false;
36+ private eventEmitter: RNC.SelectBox.EventEmitter | undefined = undefined;
37+ private cleanUpCallbacks: (() => void)[] = [];
38+ 
39+ aboutToAppear() {
40+ this.eventEmitter = new RNC.SelectBox.EventEmitter(this.ctx.rnInstance, this.tag);
41+ this.onDescriptorWrapperChange(this.ctx.descriptorRegistry.findDescriptorWrapperByTag<RNC.SelectBox.DescriptorWrapper>(this.tag)!);
42+ this.cleanUpCallbacks.push(this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag,
43+ (descriptor) => {
44+ this.onDescriptorWrapperChange(new RNC.SelectBox.DescriptorWrapper(descriptor));
45+ }
46+ ))
47+ this.cleanUpCallbacks.push(new RNC.SelectBox.CommandReceiver(this.ctx.componentCommandReceiver,
48+ this.tag).subscribe("emitNativeEvent", (argv) => {
49+ console.log(`收到 RN 传来的消息 ================ ${JSON.stringify(argv)}`);
50+ this.mutil = argv[0] || false;
51+ }))
52+ }
53+ 
54+ private onDescriptorWrapperChange(descriptorWrapper: RNC.SelectBox.DescriptorWrapper) {
55+ this.descriptorWrapper = descriptorWrapper;
56+ this.data = [];
57+ const data = this.descriptorWrapper.props.data;
58+ const value = this.descriptorWrapper.props.value;
59+ for (const item of data) {
60+ this.data.push(
61+ new SelectItems(item.id, item.text, value.includes(item.id))
62+ )
63+ }
64+ }
65+ 
66+ aboutToDisappear() {
67+ this.cleanUpCallbacks.forEach(cb => cb());
68+ }
69+ 
70+ build() {
71+ RNViewBase({ ctx: this.ctx, tag: this.tag }) {
72+ Column() {
73+ ForEach(this.data, (item: SelectItems) => {
74+ SelectItem({
75+ item: item,
76+ onSelect: (idx: number) => {
77+ for (const tmp of this.data) {
78+ if (tmp.id === idx) {
79+ tmp.selected = !tmp.selected;
80+ } else if (!this.mutil) {
81+ tmp.selected = false;
82+ }
83+ }
84+ }
85+ })
86+ }, (item: SelectItems) => item.key.toString())
87+ Button("确定")
88+ .onClick(() => {
89+ const value = this.data
90+ .filter((item: SelectItems) => item.selected)
91+ .map((item: SelectItems) => item.id)
92+ // 发送事件方式[1]
93+ // this.ctx.rnInstance.emitComponentEvent(this.tag, "change", { value })
94+ // 发送事件方式[2]
95+ this.eventEmitter!.emit("change", {
96+ value,
97+ })
98+ })
99+ }
100+ .height("100%")
101+ }
102+ }
103+}
104+ 
105+@Component
106+struct SelectItem {
107+ @ObjectLink item: SelectItems;
108+ onSelect?: (id: number) => void;
109+ 
110+ build() {
111+ Row() {
112+ Text(this.item.text)
113+ .fontColor('#0F294D')
114+ .fontSize(18)
115+ .layoutWeight(1)
116+ 
117+ Text('选中')
118+ .fontColor('#FF287DFA')
119+ .fontSize(20)
120+ .visibility(this.item.selected ? Visibility.Visible : Visibility.Hidden)
121+ }
122+ .padding({
123+ top: 24,
124+ left: 24,
125+ right: 24
126+ })
127+ .width("100%")
128+ .onClick(() => {
129+ this.onSelect?.(this.item.id)
130+ })
131+ }
132+}
@@ -0,0 +1,50 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import {
mao
maomao2025年9月23日

补充copyright

likedislike
9+ RNOHPackage,
10+ UITurboModuleContext,
11+ ComponentBuilderContext,
12+ AnyThreadTurboModuleContext,
13+ AnyThreadTurboModule,
14+ UITurboModule,
15+} from '@rnoh/react-native-openharmony';
16+import type {
17+ DescriptorWrapperFactoryByDescriptorTypeCtx,
18+ DescriptorWrapperFactoryByDescriptorType
19+} from '@rnoh/react-native-openharmony/ts';
20+import { RNC } from "./generated/ts";
21+import { SelectBox } from './SelectBox';
22+import { SampleTurboModule } from './SampleTurboModule';
23+import { SampleAnyThreadTurboModule } from './SampleAnyThreadTurboModule';
24+ 
25+@Builder
26+function buildSelectBox(ctx: ComponentBuilderContext) {
27+ SelectBox({ ctx: ctx.rnComponentContext, tag: ctx.tag, })
28+}
29+ 
30+export class ThirdPartyLibrarySamplePackage extends RNOHPackage {
31+ createDescriptorWrapperFactoryByDescriptorType(ctx: DescriptorWrapperFactoryByDescriptorTypeCtx): DescriptorWrapperFactoryByDescriptorType {
32+ const result: DescriptorWrapperFactoryByDescriptorType = {}
33+ result[RNC.SelectBox.NAME] = (ctx) => new RNC.SelectBox.DescriptorWrapper(ctx.descriptor)
34+ return result
35+ }
36+ 
37+ override getUITurboModuleFactoryByNameMap(): Map<string, (ctx: UITurboModuleContext) => UITurboModule | null> {
38+ return new Map<string, ((ctx: UITurboModuleContext) => UITurboModule)>()
39+ .set(SampleTurboModule.NAME, (ctx) => new SampleTurboModule(ctx))
40+ }
41+ 
42+ override getAnyThreadTurboModuleFactoryByNameMap(): Map<string, (ctx: AnyThreadTurboModuleContext) => AnyThreadTurboModule | null> {
43+ return new Map<string, ((ctx: AnyThreadTurboModuleContext) => AnyThreadTurboModule)>()
44+ .set(SampleAnyThreadTurboModule.NAME, (ctx) => new SampleAnyThreadTurboModule(ctx))
45+ }
46+ 
47+ createWrappedCustomRNComponentBuilderByComponentNameMap(): Map<string, WrappedBuilder<[ComponentBuilderContext]>> {
48+ return new Map().set(SelectBox.NAME, wrapBuilder(buildSelectBox))
49+ }
50+}
@@ -0,0 +1,11 @@
1+{
2+ "module": {
3+ "name": "library",
4+ "type": "har",
5+ "deviceTypes": [
6+ "default",
7+ "tablet",
8+ "2in1"
9+ ]
10+ }
11+}
@@ -0,0 +1,8 @@
1+{
2+ "float": [
3+ {
4+ "name": "page_text_font_size",
5+ "value": "50fp"
6+ }
7+ ]
8+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "page_show",
5+ "value": "page from package"
6+ }
7+ ]
8+}
@@ -0,0 +1,15 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ */
7+import NativeSampleTurboModule from "./src/specs/turbo-modules/SampleTurboModule";
8+import NativeSampleAnyThreadTurboModule from "./src/specs/turbo-modules/SampleAnyThreadTurboModule";
9+import SelectBoxNativeComponent from "./src/specs/arkts-components/SelectBox";
10+import QDGestureFloatNativeComponent from "./src/specs/cpp-components/QDGestureFloatNativeComponent"
11+ 
12+export const SampleTurboModule = NativeSampleTurboModule;
13+export const SampleAnyThreadTurboModule = NativeSampleAnyThreadTurboModule;
14+export const SelectBox = SelectBoxNativeComponent;
15+export const QDGestureFloat = QDGestureFloatNativeComponent;
@@ -0,0 +1,30 @@
1+{
2+ "name": "third-party-library-sample",
3+ "version": "1.0.0",
4+ "description": "Third party library sample package of autolinking in harmony",
5+ "main": "index.ts",
6+ "scripts": {
7+ "codegen-lib": "react-native codegen-lib-harmony --no-safety-check --npm-package-name third-party-library-sample --cpp-output-path ./harmony/library/src/main/cpp/generated --ets-output-path ./harmony/library/src/main/ets/generated --cpp-components-spec-paths ./src/specs/cpp-components --turbo-modules-spec-paths ./src/specs/turbo-modules --arkts-components-spec-paths ./src/specs/arkts-components"
8+ },
9+ "keywords": [],
10+ "author": "",
11+ "license": "ISC",
12+ "harmony": {
13+ "alias": "third-party-library-sample",
14+ "autolinking": {
15+ "cmakeLibraryTargetName": "third_party_library"
16+ }
17+ },
18+ "files": [
19+ "index.ts",
20+ "src/*",
21+ "harmony/*"
22+ ],
23+ "devDependencies": {
24+ "@react-native-community/cli": "15.0.1",
25+ "@react-native-oh/react-native-harmony-cli": "file:../react-native-oh/react-native-oh-react-native-harmony-cli-0.77.22.tgz",
26+ "react": "18.3.1",
27+ "react-native": "0.77.1"
28+ },
29+ "dependencies": {}
30+}
@@ -0,0 +1,45 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import React from 'react';
9+import type { ViewProps } from "react-native/Libraries/Components/View/ViewPropTypes";
10+import type { HostComponent } from "react-native";
11+import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
12+import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
13+import type { Int32, DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
14+ 
15+export interface SelectItems {
16+ id: Int32;
17+ text: string;
18+}
19+ 
20+export interface Res {
21+ value: Int32[];
22+}
23+ 
24+export interface NativeProps extends ViewProps, Res {
25+ data: SelectItems[];
26+ onChange: DirectEventHandler<Res>;
27+ // 添加其它 props
28+}
29+ 
30+type NativeType = HostComponent<NativeProps>;
31+ 
32+interface NativeCommands {
33+ emitNativeEvent: (
34+ viewRef: React.ElementRef<NativeType>,
35+ mutil: boolean,
36+ ) => void;
37+}
38+ 
39+export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
40+ supportedCommands: ['emitNativeEvent'],
41+});
42+ 
43+export default codegenNativeComponent<NativeProps>(
44+ "SelectBox"
45+) as HostComponent<NativeProps>;
@@ -0,0 +1,43 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE-MIT file in the root directory of this source tree.
6+ */
7+ 
8+import { ViewProps, HostComponent, ProcessedColorValue } from 'react-native';
9+import type { ColorValue } from "react-native/Libraries/StyleSheet/StyleSheet";
10+import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
11+import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
12+import type {
13+ Float,
14+ DirectEventHandler,
15+} from 'react-native/Libraries/Types/CodegenTypes';
16+ 
17+export interface ScrollEvent {
18+ offsetY: Float;
19+}
20+ 
21+export interface NativeProps extends ViewProps {
22+ stopPercent?: Float,
23+ stopPercentMax?: Float,
24+ onScroll?: DirectEventHandler<ScrollEvent>;
25+}
26+ 
27+type NativeType = HostComponent<NativeProps>;
28+ 
29+interface NativeCommands {
30+ scrollTo: (
31+ viewRef: React.ElementRef<NativeType>,
32+ offsetY: Float,
33+ animated: boolean,
34+ ) => void;
35+}
36+ 
37+export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
38+ supportedCommands: ['scrollTo'],
39+});
40+ 
41+export default codegenNativeComponent<NativeProps>(
42+ 'QDGestureFloat',
43+) as HostComponent<NativeProps>;
@@ -0,0 +1,14 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ */
7+import { TurboModuleRegistry } from 'react-native';
8+import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
9+ 
10+export interface Spec extends TurboModule {
11+ power(a: number, b: number): Promise<number>;
12+}
13+ 
14+export default TurboModuleRegistry.get<Spec>('SampleAnyThreadTurboModule')!;
@@ -0,0 +1,14 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ */
7+import { TurboModuleRegistry } from 'react-native';
8+import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
9+ 
10+export interface Spec extends TurboModule {
11+ add(a: number, b: number): number;
12+}
13+ 
14+export default TurboModuleRegistry.get<Spec>('SampleTurboModule')!;