react-native-qrcode-scanner:基于 React Native 鸿蒙生态的二维码条码扫描组件项目

二维码/条码扫描组件

分支3Tags2
文件最后提交记录最后更新时间
3 个月前
11 天前
11 天前
12 天前
1 个月前
1 个月前
1 个月前
21 天前
12 天前
11 天前
11 天前
1 个月前
11 天前
1 个月前

本项目基于 react-native-qrcode-scanner 开发。如果在使用过程中有任何问题,可以在 GitCode Issues 提交 Issue,会及时跟进。

项目介绍

react-native-qrcode-scanner 是一个二维码/条码扫描组件,提供取景预览、条码识别与结果派发、 扫码去重与重新武装(reactivate)、震动反馈、读码启停、取景框与顶/底部内容区、闪光灯与相机休眠等能力。

本仓为其 HarmonyOS 适配版本,适配基线 = 上游 1.6.0;npm 包为 @hxa-rn/react-native-qrcode-scanner,当前版本 1.6.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/react-native-qrcode-scanner@1.6.1(正式版,dist-tag latest;预发布版 0.0.1-beta 保留在 dist-tag beta),业务别名为 react-native-qrcode-scanner。在你的 RN 应用根目录执行:

npm install @hxa-rn/react-native-qrcode-scanner@1.6.1 --legacy-peer-deps

peerDependencies:{"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。

路线 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'],
},

然后补齐以下三处:

  1. 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)
  1. harmony/entry/src/main/cpp/PackageProvider.cpp:添加头文件,并把实例加入 getPackages 返回集合(RNOH 模板可加入 ManualLinkingPackage 列表):
#include "QrcodeScannerPackage.h"

// 加入已有 Package 集合:
std::make_shared<rnoh::QrcodeScannerPackage>(ctx)
  1. 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 要求);DevEco Sync 时 ohpm preInstall 钩子也要用 PATH 中的 Node.js 20+(或设置环境变量 DEVECO_PRELOAD_NODE 指向 node.exe)
DevEco Studio 需含 compatibleSdkVersion 5.1.0(18) 及以上 SDK 的版本(targetSdkVersion 未显式指定,以本机 SDK 为准)
HarmonyOS SDK API 18+(example/harmony 的 compatibleSdkVersion 为 5.1.0(18))
路径 Windows 下仓库根目录的完整路径不超过 40 个字符,否则原生构建的中间文件路径会超过系统上限

运行步骤

1. 克隆仓库

git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-qrcode-scanner.git
cd react-native-qrcode-scanner

Windows 下可在克隆时指定短目录名,例如 git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-qrcode-scanner.git C:\rqr。

2. 进入 example 目录,安装依赖

cd example
npm install --legacy-peer-deps

Example 从 npm 公仓安装 @hxa-rn/react-native-qrcode-scanner@1.6.1,不需要在仓根执行 npm pack。

3. 生成 JS Bundle

npm run dev

产物:harmony/entry/src/main/resources/rawfile/bundle.harmony.js

4. 用 DevEco Studio 打开鸿蒙工程

  • 打开 DevEco Studio,选择 example/harmony 目录
  • 首次构建请在 File → Project Structure → Signing Configs 勾选 Automatically generate signature,Apply 后 Sync
  • 等待 Sync 完成

5. 编译并运行 HAP

在 DevEco Studio 中点击运行按钮,将 HAP 安装到设备。

example_auto(自动化用例工程)步骤相同,把 example 换成 example_auto 即可,两者的依赖与构建输出相互独立。

注意:Example 已完成 HAR 依赖、Autolink、ArkTS 组件注册与权限声明,无需手动 Link。编 HAP 前必须先执行 npm run dev;改过 JS 后重新执行再运行。设备需支持 HarmonyOS Scan Kit。

常见问题

  • 找不到 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.6.0)验证范围:代码与 1.0.0 相同,仅版本号改为与上游基线一致。发布前以本包安装文件为依赖,本仓 example、example_auto(Autolink)在 DevEco Studio 6.1.1 下 assembleHap 编译通过;按「集成指南」路线 B 改接的宿主副本(Manual Link)编译通过的记录来自 1.0.0。

权限:ohos.permission.CAMERA(取景与识码依赖)、ohos.permission.VIBRATE(震动反馈),宿主 module.json5 需声明。

  • 相机来源:当前适配包默认使用内置 Scan Kit 相机组件;cameraProvider 用于自定义来源,不依赖 react-native-camera。
  • 未授权 / 待授权两态视图:鸿蒙侧新增 QRScannerPermission TurboModule,授权由 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 与 peer react-native-camera 在鸿蒙实现中均未使用,本包不声明这些依赖(授权改由内置 QRScannerPermission TurboModule 提供)。

开源 License

本项目基于 MIT License,与上游react-native-qrcode-scanner 保持一致,请自由地享受和参与开源。

问题反馈渠道

使用过程中发现任何问题,欢迎在GitCode 提交 Issue,也欢迎提交 PR 参与共建。