@hxa-rn/react-native-qrcode-scanner
v1.0.0
Published
A QR code scanner for React Native.
Downloads
242
Readme
本项目基于 react-native-qrcode-scanner 开发。如果在使用过程中有任何问题,可以在 GitCode Issues 提交 Issue,会及时跟进。
项目介绍
react-native-qrcode-scanner 是一个二维码/条码扫描组件,提供取景预览、条码识别与结果派发、
扫码去重与重新武装(reactivate)、震动反馈、读码启停、取景框与顶/底部内容区、闪光灯与相机休眠等能力。
本仓为其 HarmonyOS 适配版本,适配基线 = 上游 1.6.0;npm 包为 @hxa-rn/react-native-qrcode-scanner,当前版本 1.0.0。
架构形态(务必先读):上游自身零原生代码(index.js 348 行 JS 包装组件),
把「取景 + 出码」全权委托给 peer 依赖 react-native-camera。该 peer 已归档停维、无 RNOH 移植、
103 个原生文件的旧架构,照搬即强阻塞。
本适配采用依赖倒置:把相机来源抽象成可注入的 cameraProvider 契约,鸿蒙侧用 HarmonyOS Scan Kit
的 customScan 自建 Fabric 原生组件 QRScanKitCamera(customScan + XComponent + codegen C++ glue +
emitComponentEvent('barCodeRead'))。本库自身的控制逻辑(去重 / 震动 / reactivate)与解码流映射无平台障碍。
集成指南
本节用于接入已有 RN 鸿蒙应用;运行本仓库示例请参见「快速验证(运行 Example)」。
1. 安装适配包
npm 包:@hxa-rn/[email protected](正式版,dist-tag latest;预发布版 0.0.1-beta 保留在 dist-tag beta),业务别名为 react-native-qrcode-scanner。在你的 RN 应用根目录执行:
npm install @hxa-rn/[email protected] --legacy-peer-depspeerDependencies:{"react-native":">=0.72","react":"*"}。还需已有可运行的 RNOH 宿主工程(示例基线为 RN 0.72.5 + RNOH 0.72.143)。
包内含源码 HAR harmony/qrcode_scanner.har(ArkTS 源码 + C++ 源码,由宿主工程一起编译),其 ohpm 模块名为 @oh-rn/react-native-qrcode-scanner(与 npm 包名不同,下文 ohpm 依赖、ArkTS/C++ 导入一律用 ohpm 名)。
2. 配置 Metro 并导入
适配包已声明 harmony.alias。宿主 metro.config.js 合并以下配置,并保留原有的自定义设置:
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config');
module.exports = mergeConfig(
getDefaultConfig(__dirname),
createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
}),
);业务代码(两种写法等价,createHarmonyMetroConfig 会把别名解析到本包):
import QRCodeScanner, {checkCameraPermission, requestCameraPermission} from 'react-native-qrcode-scanner';
// 或
import QRCodeScanner from '@hxa-rn/react-native-qrcode-scanner';3. 声明 HAR 依赖
在宿主鸿蒙工程根 harmony/oh-package.json5 与 harmony/entry/oh-package.json5 的 dependencies 追加下项(路径相对于各自文件所在目录,按宿主实际 node_modules 位置调整),并在根 overrides 中将 @rnoh/react-native-openharmony 指向宿主正在使用的同一 RNOH HAR(勿混用 debug/release 或不同版本):
// harmony/oh-package.json5
"dependencies": {
"@oh-rn/react-native-qrcode-scanner": "file:../node_modules/@hxa-rn/react-native-qrcode-scanner/harmony/qrcode_scanner.har"
},
"overrides": {
"@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony_release.har"
}
// harmony/entry/oh-package.json5
"dependencies": {
"@oh-rn/react-native-qrcode-scanner": "file:../../node_modules/@hxa-rn/react-native-qrcode-scanner/harmony/qrcode_scanner.har"
}然后在 harmony 目录执行 ohpm install --all 并同步工程。不要把 file: 指向解包后的源码目录 harmony/qrcode_scanner/(ohpm 会建越界 junction,ArkTS 编译报 EINVAL),只依赖 .har。
4. 注册原生 Package(Autolink 与 Manual 二选一)
路线 A:Autolink(推荐,与本仓示例一致)
本包声明了 Autolinking 元数据:CMake 目标 qrcode_scanner,ohpm 包 @oh-rn/react-native-qrcode-scanner,C++ / ETS 类 QrcodeScannerPackage。宿主 harmony/hvigorfile.ts 的 createRNOHModulePlugin 启用 autolinking: {} 即可;构建时 hvigor 会生成 entry/src/main/ets/RNOHPackagesFactory.ets、entry/src/main/cpp/RNOHPackagesFactory.h 与 autolinking.cmake。宿主 CMakeLists.txt 需 include("${CMAKE_CURRENT_SOURCE_DIR}/autolinking.cmake") 并 autolink_libraries(rnoh_app),PackageProvider.cpp 与 RNPackagesFactory.ets 需合并 createRNOHPackages(ctx) 的返回值(RNOH 模板已含)。此时不要再做下面的手工注册,否则会重复注册(CMake 报 binary directory … is already used)。
路线 B:Manual Link
宿主已启用 Autolinking 时,先把本包排除,避免与手工注册冲突(排除项写 npm 包名):
autolinking: {
excludeNpmPackages: ['@hxa-rn/react-native-qrcode-scanner'],
},然后补齐以下三处:
harmony/entry/src/main/cpp/CMakeLists.txt:添加子目录,并在已有add_library(rnoh_app ...)之后链接:
set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
add_subdirectory("${OH_MODULE_DIR}/@oh-rn/react-native-qrcode-scanner/src/main/cpp" ./qrcode_scanner)
# 放在 rnoh_app 目标创建之后:
target_link_libraries(rnoh_app PUBLIC qrcode_scanner)harmony/entry/src/main/cpp/PackageProvider.cpp:添加头文件,并把实例加入getPackages返回集合(RNOH 模板可加入ManualLinkingPackage列表):
#include "QrcodeScannerPackage.h"
// 加入已有 Package 集合:
std::make_shared<rnoh::QrcodeScannerPackage>(ctx)harmony/entry/src/main/ets/RNPackagesFactory.ets:添加默认导入,并把实例加入createRNPackages返回数组:
import QrcodeScannerPackage from '@oh-rn/react-native-qrcode-scanner';
// 加入 createRNPackages(ctx) 返回数组:
new QrcodeScannerPackage(ctx)5. 注册 ArkTS 取景组件(两条路线都必须)
取景组件 QRScanKitCamera 由 ArkTS 实现。包内 QrcodeScannerPackage 已登记组件描述符与构建器,但宿主 RNApp 必须声明 arkTsComponentNames,Autolink 不会替你改宿主页面。在宿主承载 RNApp 的页面(示例为 harmony/entry/src/main/ets/pages/Index.ets)按本仓示例写法补齐三处(示例同时在宿主提供构建器,已随示例编译验证):
import { ComponentBuilderContext, RNApp } from '@rnoh/react-native-openharmony';
import { QRScanKitCamera, QR_SCAN_KIT_CAMERA_TYPE } from '@oh-rn/react-native-qrcode-scanner';
// 1) 声明由 ArkTS 实现的组件名(与 JS 侧 codegenNativeComponent('QRScanKitCamera') 逐字一致)
const ARK_TS_COMPONENT_NAMES: string[] = [QR_SCAN_KIT_CAMERA_TYPE];
// 2) 组件构建器:C-API 混合方案要求外层 Stack 且 position 为 (0,0)
@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
Stack() {
if (ctx.componentName === QR_SCAN_KIT_CAMERA_TYPE) {
QRScanKitCamera({ ctx: ctx.rnComponentContext, tag: ctx.tag })
}
}
.position({ x: 0, y: 0 })
}
const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
// 3) 传给 RNApp(其余参数保持宿主原样)
RNApp({
rnInstanceConfig: {
createRNPackages,
enableCAPIArchitecture: true,
arkTsComponentNames: ARK_TS_COMPONENT_NAMES,
// ...
},
wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
// ...
})漏掉 arkTsComponentNames 时,RNOH 会去找不存在的 C++ ComponentInstance,取景区不渲染。宿主已有其他 ArkTS 组件时,把名字并入同一数组、在同一构建器里追加分支。
6. 权限
在宿主 harmony/entry/src/main/module.json5 的 requestPermissions 声明(本 HAR 未声明任何权限,须由宿主声明):
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_permission_reason",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
},
{ "name": "ohos.permission.VIBRATE" }
]camera_permission_reason 需在宿主 resources/base/element/string.json 中定义。CAMERA 为 user_grant,运行时通过 checkCameraPermission() / requestCameraPermission() 检查与申请(组件挂载时也会自动发起);VIBRATE 供 vibrate(默认 true)使用。设备需支持 HarmonyOS Scan Kit。
完成以上步骤后重新生成宿主 Bundle、构建并安装 HAP;只刷新 JS 不会加载新增的原生模块。
使用说明
import React from 'react';
import {Text} from 'react-native';
import QRCodeScanner, {QRScanKitCamera as QRScanKitCameraProvider} from 'react-native-qrcode-scanner';
export default function App() {
return (
<QRCodeScanner
cameraProvider={QRScanKitCameraProvider}
onRead={(e) => console.log(e.data, e.type, e.bounds)}
vibrate
reactivate
reactivateTimeout={2000}
showMarker
topContent={<Text>将二维码放入框内</Text>}
bottomContent={<Text>扫描中…</Text>}
/>
);
}派发链路(禁止绕过):
Scan Kit customScan 回调 → QRScanKitCamera.ets → eventEmitter.emit('barCodeRead')(codegen 封装的 emitComponentEvent)
→ JS 包装 → cameraProvider 的 onBarCodeRead
→ QRCodeScanner._handleBarCodeRead(去重锁 / 震动 / reactivate 定时器 / disable 门控)
→ props.onRead若图省事直接调 onRead,「去重与重新武装」「震动与启停」两组行为会全部假通过——它们判的正是被绕过的那一段。
接口文档
公开接口 = 默认导出组件的 props(25 个) + 公开实例方法(3 个),另有适配层新增的 cameraProvider。主要项:
| API | 类型 | 默认值 | 说明 |
|---|---|---|---|
| onRead | (e: BarCodeReadEvent) => void | 打印日志 | 必填。识别到条码时派发一次 |
| cameraProvider | Component | 内置 Scan Kit 相机组件 | 可选的自定义相机来源注入点(适配层新增) |
| vibrate | boolean | true | 识别成功是否震动 |
| reactivate | boolean | false | 一次识别后是否自动重新武装 |
| reactivateTimeout | number | 0 | 重新武装延时(ms) |
| cameraTimeout | number | 0 | 相机空闲休眠时长(ms);0 = 不休眠 |
| showMarker | boolean | false | 取景框总开关 |
| customMarker | Element | 未定义 | 自定义取景框(受 showMarker 门控) |
| topContent / bottomContent | Element | string | 未定义 | 顶/底部内容区 |
| containerStyle / cameraStyle / markerStyle / topViewStyle / bottomViewStyle | Style | — | 各层样式 |
| fadeIn | boolean | true | 取景区淡入动画 |
| flashMode | 'off'\|'on'\|'auto'\|'torch' | 'auto' | 闪光灯档位(鸿蒙侧 auto 按环境亮度自动开关补光,见「约束与限制」) |
| cameraType | 'front'\|'back' | 'back' | 鸿蒙侧仅后置 |
| notAuthorizedView / pendingAuthorizationView | Element | 内置视图 | 未授权 / 待授权视图,鸿蒙侧由 QRScannerPermission TurboModule 提供授权状态 |
| cameraTimeoutView | Element | 内置「Tap to activate camera」 | 相机休眠时呈现 |
| permissionDialogTitle / permissionDialogMessage / buttonPositive / checkAndroid6Permissions | — | 'Info' / 'Need camera permission' / 'OK' / false | 源平台授权对话框参数,鸿蒙侧无对应表现 |
| cameraProps | object | {} | 透传给底层相机组件的任意 props |
| disable() | 方法 | — | 停止读码(门控整个读码处理) |
| enable() | 方法 | — | 恢复读码 |
| reactivate() | 方法 | — | 手动重新武装(解锁去重) |
onRead 事件对象:
| 字段 | 类型 | 说明 |
|------|------|------|
| data | string | 条码原始内容(Scan Kit ScanResult.originalValue) |
| type | string | 码制字符串(scanType 经映射;无对等者为 unknown) |
| bounds | { width, height, origin } | 码区域与四角点(Scan Kit scanCodeRect 折算,Android 形态) |
Fabric 原生组件契约(QRScanKitCamera,供接入方了解,不直接对外):
props flashMode(string,默认 'auto',上游 flashMode 原值直传,由 ArkTS 侧折算)、scanTypesCsv(string,默认 '',Scan Kit ScanType 数值 CSV,空 = 全部);
事件 onBarCodeRead(DirectEventHandler,bounds.origin 以 originJson JSON 字符串承载)、onCameraError(DirectEventHandler,{ code, message },经 cameraProps 透传可见)。
快速验证(运行 Example)
前置条件
- Node.js >= 20(以示例 engines 为准)与 npm,DevEco Studio 及配套 ohpm / SDK。示例的 ohpm
preInstall钩子会用 PATH 中的 Node.js >= 20 补齐依赖,请确保启动 DevEco Studio 时 PATH 能找到它(或设置环境变量DEVECO_PRELOAD_NODE指向node.exe)。 - 示例版本:React Native
0.72.5、RNOH0.72.143;本包从 npmjs 安装@hxa-rn/[email protected]。 - 鸿蒙工程配置:compatibleSdkVersion=5.1.0(18);targetSdkVersion 未在产品配置显式指定,以本机 SDK 同步结果为准。设备需支持 HarmonyOS Scan Kit。
- 在 DevEco Studio 中配置与当前应用包名匹配的签名,不能直接使用其他电脑上的证书路径。
运行步骤
1. 克隆仓库(默认分支不是适配分支,必须带 -b br_rnoh0.72)
git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-qrcode-scanner.git
cd react-native-qrcode-scanner以下步骤使用 example 展示工程;example_auto(自动化用例工程)步骤相同,两者的依赖与构建输出独立,不要混用。Windows 建议使用较短目录,避免 RNOH 原生构建中间路径过长。
2. 安装示例依赖(本包从 npm 安装,无需在仓库根目录打包)
cd example
npm install --legacy-peer-deps3. 生成 JS Bundle
npm run dev确认生成 example/harmony/entry/src/main/resources/rawfile/bundle.harmony.js。先解决命令报错,再进入原生构建。
4. 同步与运行鸿蒙工程
用 DevEco Studio 打开 example/harmony,完成依赖同步(ohpm install 的 preInstall 钩子会检查并补齐 npm 依赖与 Bundle)。也可在该目录执行 ohpm install --all 后再同步。保留示例已有的 RNOH HAR 路径和版本。
示例已按「集成指南」第 3~6 步完成 HAR 依赖、Autolink、ArkTS 组件注册与权限声明,无需重复添加。选择设备、配置签名并点击运行,生成并安装 HAP。修改 JS 后重新生成 Bundle;修改原生依赖后重新构建安装 HAP。
常见问题
- 找不到 JS 模块:检查适配包安装、Metro 别名及对应入口,修改 Metro 后重启打包进程。
- 找不到 HAR:核对
node_modules/@hxa-rn/react-native-qrcode-scanner/harmony/qrcode_scanner.har是否存在;不要将example_auto的 node_modules 当成example的依赖。 - 取景区空白:检查「集成指南」第 5 步的
arkTsComponentNames与组件构建器,以及相机权限是否已授予。 - 原生模块未注册或重复注册:检查 HAR、CMake、C++ 和 ETS 注册,Autolink 与 Manual 只能二选一,然后重建 HAP。
- DevEco 同步时报
Node.js 20 or newer with npm is required:安装 Node.js 20+ 并重启 DevEco Studio,或设置DEVECO_PRELOAD_NODE。 - SDK / 签名报错:按当前工程配置安装对应 SDK,并重新配置本机签名。
- Ninja
.rsp创建失败:查看完整日志;确认是路径长度问题后,缩短工程目录、清理旧路径构建缓存再构建。
约束与限制
兼容性:RNOH 0.72.143 / RN 0.72.5;HarmonyOS Scan Kit customScan(since API 4.1.0(11))。
本版本(1.0.0)验证范围:发布前以本包安装文件为依赖,本仓 example、example_auto(Autolink)及按「集成指南」路线 B 改接的宿主副本(Manual Link),在 DevEco Studio 6.1.1 下 assembleHap 编译通过。
权限:ohos.permission.CAMERA(取景与识码依赖)、ohos.permission.VIBRATE(震动反馈),宿主 module.json5 需声明。
- 相机来源:当前适配包默认使用内置 Scan Kit 相机组件;
cameraProvider用于自定义来源,不依赖react-native-camera。 - 未授权 / 待授权两态视图:鸿蒙侧新增
QRScannerPermissionTurboModule,授权由 JS 侧发起 (与上游 iOS 分支同构),checkCameraPermission()/requestCameraPermission()返回真实相机授权状态,notAuthorizedView/pendingAuthorizationView两态视图分支可按授权状态呈现。 flashMode='auto'自动补光:使用customScan.on('lightingFlash')(5.0.0(12) 起)订阅环境亮度,暗则openFlashLight()、亮则closeFlashLight(),切出 auto 或组件销毁时退订。- 仅支持后置相机:
cameraType='front'无效,customScan API 无前置相机参数。 - 部分码制无对等映射:QR / AZTEC / CODE39 / 93 / 128 / DATAMATRIX / EAN8 / 13 / ITF14 / PDF417 / UPC_E
一一对应;CODABAR / UPC_A / 聚合枚举归
unknown(诚实归类,不伪造映射);code39mod43/interleaved2of5无 Scan Kit 对等,已剔除。 disable()的语义:它门控的是整个读码处理(既不派发也不震动), 尽管上游内部字段名为disableVibrationByUser——该命名有误导性,只按「关震动」理解会用错。- 自定义取景框受总开关门控:
showMarker关闭时,即便传了customMarker也不呈现。 static defaultProps禁止引用文件底部的styles:类属性初始化器在类定义时即求值, 那一刻const styles = StyleSheet.create({...})尚未初始化 → 取到undefined→ 模块加载即抛 → 首装纯白屏。三个默认视图必须写成内联样式对象。- 依赖精简:上游声明的
@react-native-community/async-storage、prop-types、react-native-permissions与 peerreact-native-camera在鸿蒙实现中均未使用,本包不声明这些依赖(授权改由内置QRScannerPermissionTurboModule 提供)。
开源 License
本项目基于 MIT License,与上游react-native-qrcode-scanner 保持一致,请自由地享受和参与开源。
问题反馈渠道
使用过程中发现任何问题,欢迎在GitCode 提交 Issue,也欢迎提交 PR 参与共建。
