2 回复
import { scanCore, scanBarcode, customScan, detectBarcode } from '@kit.ScanKit';
// 导入功能涉及的权限申请、回调接口
import { router, promptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common, abilityAccessCtrl } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { uri } from '@kit.ArkTS';
[@Entry](/user/Entry)
[@Component](/user/Component)
struct CustomScanDemo {
[@State](/user/State) userGrant: boolean = false
[@State](/user/State) surfaceId: string = ''
[@State](/user/State) isShowBack: boolean = false
[@State](/user/State) isFlashLightEnable: boolean = false
[@State](/user/State) isFlashLightOpen: boolean = false
// 设置预览流高度,默认单位:vp
[@State](/user/State) cameraHeight: number = 640
// 设置预览流宽度,默认单位:vp
[@State](/user/State) cameraWidth: number = 360
private mXComponentController: XComponentController = new XComponentController()
private TAG: string = '[CustomScanDemo]'
async showScanResult(result: Array<scanBarcode.ScanResult>) {
if (result.length > 0) {
// 打印测试结果,可在获取到结果后做相应处理
hilog.info(0x0001, this.TAG, 'scan result: %{public}s', JSON.stringify(result));
//获取到扫描结果后暂停相机流
try {
customScan.stop().then(() => {
hilog.info(0x0001, this.TAG, 'stop success!');
this.isFlashLightEnable = false;
}).catch((error: BusinessError) => {
hilog.error(0x0001, this.TAG, 'stop failed error: %{public}s ', JSON.stringify(error));
})
} catch (error) {
hilog.error(0x0001, this.TAG, 'stop failed error: %{public}s ', JSON.stringify(error));
}
// 使用toast显示出扫码结果
try {
promptAction.showToast({
message: JSON.stringify(result),
duration: 5000
});
} catch (error) {
hilog.error(0x0001, this.TAG, 'showToast error: %{public}s ', JSON.stringify(error));
}
this.isShowBack = true;
this.isFlashLightEnable = false;
router.pushUrl({
url: "pages/DefaultScanDemo"
})
}
}
async reqPermissionsFromUser(): Promise<number[]> {
hilog.info(0x0001, this.TAG, 'reqPermissionsFromUser start ')
let context = getContext() as common.UIAbilityContext;
let atManager = abilityAccessCtrl.createAtManager();
let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.CAMERA']);
return grantStatus.authResults;
}
// 申请相机权限
async requestCameraPermission() {
let grantStatus = await this.reqPermissionsFromUser()
for (let i = 0; i < grantStatus.length; i++) {
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
hilog.info(0x0001, this.TAG, 'requestPermissionsFromUser success');
this.userGrant = true;
}
}
}
setDisplay() {
// 默认竖屏
this.cameraHeight = 800
this.cameraWidth = 450
}
async onPageShow() {
await this.requestCameraPermission();
let options: scanBarcode.ScanOptions = {
scanTypes: [scanCore.ScanType.ALL],
enableMultiMode: true,
enableAlbum: true
}
this.setDisplay();
try {
customScan.init(options);
} catch (error) {
hilog.error(0x0001, this.TAG, 'init fail, error: %{public}s ', JSON.stringify(error));
}
}
async onPageHide() {
// 页面消失或隐藏时,停止并释放相机流
this.userGrant = false;
try {
await customScan.stop();
} catch (error) {
hilog.error(0x0001, this.TAG, 'Catch: stop error %{public}s', JSON.stringify(error));
}
try {
customScan.release().then(() => {
hilog.info(0x0001, this.TAG, 'release success!');
this.isFlashLightEnable = false;
}).catch((error: BusinessError) => {
hilog.error(0x0001, this.TAG, 'release failed error: %{public}s ', JSON.stringify(error));
})
} catch (error) {
hilog.error(0x0001, this.TAG, 'Catch: release error %{public}s', JSON.stringify(error));
}
}
// 自定义扫码界面的顶部返回按钮和扫码提示
[@Builder](/user/Builder)
TopTool() {
Column() {
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
Text('返回').fontColor(Color.White).fontWeight(FontWeight.Bold)
.onClick(async () => {
router.back();
})
}.padding({ left: 24, right: 24, top: 40 })
Column() {
Text('扫描二维码/条形码').fontColor(Color.White).fontWeight(FontWeight.Bold)
Text('对准二维码/条形码,即可自动扫描').fontColor(Color.White).fontWeight(FontWeight.Bold)
}.margin({ left: 24, right: 24, top: 24 })
}
.height(146)
.width('100%')
}
build() {
Stack() {
if (this.userGrant) {
Column() {
XComponent({
id: 'componentId',
type: 'surface',
controller: this.mXComponentController
})
.onLoad(async () => {
hilog.info(0x0001, this.TAG, 'onLoad is called');
// 获取XComponent组件的surfaceId
this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
hilog.info(0x0001, this.TAG, 'surfaceId: %{public}s ', this.surfaceId);
let viewControl: customScan.ViewControl = {
width: this.cameraWidth,
height: this.cameraHeight,
surfaceId: this.surfaceId
};
// 启动相机进行扫码
try {
this.isFlashLightEnable = true;
customScan.start(viewControl)
.then(async (result: Array<scanBarcode.ScanResult>) => {
// 处理扫码结果
this.showScanResult(result);
})
} catch (error) {
hilog.error(0x0001, this.TAG, 'start fail, error: %{public}s ', JSON.stringify(error));
}
})// XComponent宽、高,默认单位vp,支持px、lpx、vp
.width(this.cameraWidth)
.height(this.cameraHeight)
.position({ x: 0, y: 0 })
}
.height('100%')
.width('100%')
}
Column() {
this.TopTool()
Column() {
}
.layoutWeight(1)
.width('100%')
// 闪光灯按钮,启动相机流后才能使用
Column() {
if (this.isFlashLightOpen) {
Image($r('app.media.flash_off')).width(30).height(30)
} else {
Image($r('app.media.flash_on')).width(30).height(30)
}
}
.justifyContent(FlexAlign.Center)
.width(40)
.height(40)
.borderWidth(1)
.borderRadius(20)
.onClick(() => {
// 根据当前闪光灯状态,选择打开或关闭闪关灯
try {
if (customScan.getFlashLightStatus()) {
customScan.closeFlashLight();
} else {
customScan.openFlashLight();
}
this.isFlashLightOpen = !this.isFlashLightOpen;
} catch (error) {
hilog.error(0x0001, this.TAG, 'flashLight control failed, error: %{public}s', JSON.stringify(error));
}
}).position({ x: 90, y: 600 })
.visibility((this.userGrant && this.isFlashLightEnable) ? Visibility.Visible : Visibility.None)
// 系统图库
Column() {
Image($r('app.media.ic_public_picture')).width(30).height(30)
}
.justifyContent(FlexAlign.Center)
.width(40)
.height(40)
.borderWidth(1)
.borderRadius(20)
.onClick(() => {
if (customScan.getFlashLightStatus()) {
customScan.closeFlashLight();
this.isFlashLightOpen = false;
}
// 打开系统图库,选择图片扫描
this.scanSystemPic()
}).position({ x: 270, y: 600 })
.visibility((this.userGrant && this.isFlashLightEnable) ? Visibility.Visible : Visibility.None)
// 重新扫码按钮
Button('Scan')
.onClick(() => {
// 点击按钮重启相机流,重新扫码
try {
this.isFlashLightEnable = true;
customScan.start({ width: 1920, height: 1080, surfaceId: this.surfaceId })
.then(async (result: Array<scanBarcode.ScanResult>) => {
// 处理扫码结果
this.showScanResult(result);
})
} catch (error) {
hilog.error(0x0001, this.TAG, 'start fail, error: %{public}s ', JSON.stringify(error));
}
this.isShowBack = false;
})
.margin({ bottom: 30 })
.visibility(this.isShowBack ? Visibility.Visible : Visibility.None)
}
}
// 建议相机流设置为全屏
.width('100%')
.height('100%')
}
async scanSystemPic() {
try {
// 1、选择系统图片文件,获取文件管理器返回的文件可读uri
let selectUri: string = '';
let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
let photoPicker = new photoAccessHelper.PhotoViewPicker();
await photoPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
hilog.info(0x0001, this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));
if (photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {
selectUri = photoSelectResult.photoUris[0];
}
}).catch((err: BusinessError) => {
hilog.error(0x0001, this.TAG, 'PhotoViewPicker.select failed with err: ' + JSON.stringify(err));
});
if(!selectUri) {
hilog.error(0x0001, this.TAG, 'scanSystemPic failed, selectUri is null.');
return;
}
// 2、识别图片
try {
// 定义识码参数options
let options: scanBarcode.ScanOptions = { scanTypes: [scanCore.ScanType.ALL], enableMultiMode: true, enableAlbum: true }
// 定义识码参数inputImage,其中uri为picker选择图片
let inputImage: detectBarcode.InputImage = { uri: selectUri }
// 调用图片识码接口
await detectBarcode.decode(inputImage, options).then((result: Array<scanBarcode.ScanResult>) => {
hilog.info(0x0001, this.TAG, 'Promise scan result: %{public}s', JSON.stringify(result));
// 处理扫码结果
this.showScanResult(result);
}).catch((failResult: BusinessError) => {
hilog.error(0x0001, this.TAG, 'Promise scan result: %{public}s', JSON.stringify(failResult));
});
} catch (error) {
hilog.error(0x0001, this.TAG, 'Catch: failResult: %{public}s', JSON.stringify(error));
}
} catch (error) {
hilog.error(0x0001, this.TAG, 'start fail, error: %{public}s ', JSON.stringify(error));
}
}
}
更多关于HarmonyOS 鸿蒙Next 自定义扫码Demo的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next系统中,自定义扫码Demo的实现主要依赖于ArkUI框架提供的组件和能力。以下是一个简要步骤,用于指导如何快速构建自定义扫码Demo:
-
项目创建:使用DevEco Studio创建新的HarmonyOS项目,选择“Empty Ability”模板。
-
界面设计:在
resources/base/layout
目录下创建或编辑XML布局文件,添加一个CameraKit
组件用于扫码界面。 -
权限配置:在
config.json
文件中添加相机权限,确保应用有权限访问相机。 -
逻辑实现:
- 在对应的
.ets
文件中,通过@Entry
装饰的组件类实现扫码逻辑。 - 监听
CameraKit
的扫码结果事件,通过事件回调获取扫码内容。 - 对扫码结果进行解析或处理,如跳转到对应页面或显示结果。
- 在对应的
-
运行测试:将项目部署到鸿蒙设备或模拟器上,运行应用并测试扫码功能。
示例代码片段(仅示意,非完整代码):
@Entry
@Component
struct ScanCodePage {
@State scanResult: string = '';
@ConsumeScanResult
handleScanResult(event: CustomEvent<string>) {
this.scanResult = event.detail;
}
build() {
Column() {
CameraKit()
.on('scanResult', this.handleScanResult);
Text(this.scanResult);
}.padding(16);
}
}
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html