自定义界面扫码,满足应用个性化定制需求

news/2024/11/15 0:48:51/文章来源:https://www.cnblogs.com/HarmonyOSSDK/p/18404269

二维码识别技术已经成为我们日常生活中不可或缺的一部分,广泛应用于支付、交通、餐饮、生活服务以及智能家居等领域。它不仅是移动应用的重要流量入口,更是连接线上线下世界的桥梁。

不同的App在扫码界面的设计上各展其特色,从页面元素到交互方式,都体现了开发者对用户体验的重视。然而,标准化的扫码界面往往难以满足开发者对个性化定制的追求。例如,开发者可能希望调整扫码页面的标题、优化扫码框的尺寸与位置,甚至定制扫码框的颜色和动画效果。

HarmonyOS SDK 统一扫码服务(Scan Kit)提供了自定义界面扫码能力,开发者可以自行定义扫码的界面样式,让扫码界面更美观,和开发者的应用风格更加匹配。

自定义界面扫码能力提供扫码相机流控制接口,支持相机流的初始化、开启、暂停、释放功能;支持闪光灯的状态获取、开启、关闭;支持变焦比的获取和设置;支持对条形码、二维码等进行扫码识别,并获得码类型、码值、码位置信息、相机预览流(YUV)。该能力可用于单码和多码的扫描识别。

业务流程

image

开发步骤

自定义界面扫码接口支持自定义UI界面,识别相机流中的条形码,二维码等,并返回码图的值、类型、码的位置信息(码图最小外接矩形左上角和右下角的坐标)以及相机预览流(YUV)。

以下示例为调用自定义界面扫码接口拉起相机流并返回扫码结果和相机预览流(YUV)。

1.在开发应用前,需要先申请相机相关权限,确保应用拥有访问相机的权限。在"module.json5"文件中配置相机权限,具体配置方式,请参见声明权限。

image

2.使用接口requestPermissionsFromUser去校验当前用户是否已授权。具体申请方式及校验方式,请参见向用户申请授权。

3.导入自定义界面扫码接口以及相关接口模块,导入方法如下。

import { scanCore, scanBarcode, customScan } from '@kit.ScanKit';
// 导入功能涉及的权限申请、回调接口
import { router, promptAction, display } from '@kit.ArkUI';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common, abilityAccessCtrl } from '@kit.AbilityKit';

4.遵循业务流程完成自定义界面扫码功能。

通过Promise方式回调,调用自定义界面扫码接口拉起相机流并返回扫码结果。

@Entry
@Component
struct CustomScanPage {@State userGrant: boolean = false@State surfaceId: string = ''@State isShowBack: boolean = false@State isFlashLightEnable: boolean = false@State isSensorLight:boolean = false// 设置预览流高度,默认单位:vp@State cameraHeight: number = 640// 设置预览流宽度,默认单位:vp@State cameraWidth: number = 360@State cameraOffsetX: number = 0@State cameraOffsetY: number = 0@State zoomValue: number = 1@State setZoomValue: number = 1@State scaleValue: number = 1@State pinchValue: number = 1@State displayHeight: number = 0@State displayWidth: number = 0private mXComponentController: XComponentController = new XComponentController()private TAG: string = '[customScanPage]'async showScanResult(result: Array<scanBarcode.ScanResult>) {if (result.length > 0) {// 获取到扫描结果后暂停相机流customScan.stop().then(() => {hilog.info(0x0001, this.TAG, 'Succeeded in stopping customScan by promise!');}).catch((error: BusinessError) => {hilog.error(0x0001, this.TAG,`Failed to stop customScan by promise. Code: ${error.code}, message: ${error.message}`);})// 使用toast显示出扫码结果promptAction.showToast({message: JSON.stringify(result),duration: 5000});this.isShowBack = true; }}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, 'Succeeded in getting permissions.');this.userGrant = true;}}}setDisplay() {// 默认竖屏let displayClass = display.getDefaultDisplaySync();this.displayHeight = px2vp(displayClass.height);this.displayWidth = px2vp(displayClass.width);let maxLen: number = Math.max(this.displayWidth, this.displayHeight);let minLen: number = Math.min(this.displayWidth, this.displayHeight);const RATIO: number = 16 / 9;this.cameraHeight = maxLen;this.cameraWidth = maxLen / RATIO;this.cameraOffsetX = (minLen - this.cameraWidth) / 2;}async onPageShow() {await this.requestCameraPermission();let options: scanBarcode.ScanOptions = {scanTypes: [scanCore.ScanType.ALL],enableMultiMode: true,enableAlbum: true}this.setDisplay();// 自定义初始化接口customScan.init(options);}async onPageHide() {// 页面消失或隐藏时,停止并释放相机流this.userGrant = false;this.isFlashLightEnable = false;this.isSensorLight = false;try {customScan.off('lightingFlash');} catch (error) {hilog.error(0x0001, this.TAG, `Failed to off lightingFlash. Code: ${error.code}, message: ${error.message}`);}await customScan.stop();// 自定义相机流释放接口customScan.release().then(() => {hilog.info(0x0001, this.TAG, 'Succeeded in releasing customScan by promise.');}).catch((error: BusinessError) => {hilog.error(0x0001, this.TAG,`Failed to release customScan by promise. Code: ${error.code}, message: ${error.message}`);})}// 自定义扫码界面的顶部返回按钮和扫码提示@BuilderTopTool() {Column() {Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {Text('返回').onClick(async () => {router.back();})}.padding({ left: 24, right: 24, top: 40 })Column() {Text('扫描二维码/条形码')Text('对准二维码/条形码,即可自动扫描')}.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, 'Succeeded in loading, onLoad is called.');// 获取XComponent组件的surfaceIdthis.surfaceId = this.mXComponentController.getXComponentSurfaceId();hilog.info(0x0001, this.TAG, `Succeeded in getting surfaceId: ${this.surfaceId}`);let viewControl: customScan.ViewControl = {width: this.cameraWidth,height: this.cameraHeight,surfaceId: this.surfaceId};// 启动相机进行扫码// 通过Promise方式回调customScan.start(viewControl).then(async (result: Array<scanBarcode.ScanResult>) => {// 处理扫码结果this.showScanResult(result);});customScan.on('lightingFlash', (error, isLightingFlash) => {if (error) {hilog.error(0x0001, this.TAG, `Failed to on lightingFlash. Code: ${error.code}, message: ${error.message}`);return;}if (isLightingFlash) {this.isFlashLightEnable = true;} else {if (!customScan.getFlashLightStatus()) {this.isFlashLightEnable = false;}}this.isSensorLight = isLightingFlash;});})// XComponent宽、高,默认单位vp,支持px、lpx、vp.width(this.cameraWidth).height(this.cameraHeight).position({ x: this.cameraOffsetX, y: this.cameraOffsetY })}.height('100%').width('100%')}Column() {this.TopTool()Column() {}.layoutWeight(1).width('100%')Column() {Row() {// 闪光灯按钮,启动相机流后才能使用Button('FlashLight').onClick(() => {// 根据当前闪光灯状态,选择打开或关闭闪关灯if (customScan.getFlashLightStatus()) {customScan.closeFlashLight();setTimeout(() => {this.isFlashLightEnable = this.isSensorLight;}, 200);} else {customScan.openFlashLight();}}).visibility((this.userGrant && this.isFlashLightEnable) ? Visibility.Visible : Visibility.None)// 重新扫码按钮Button('Scan').onClick(() => {// 点击按钮重启相机流,重新扫码customScan.start({ width: 1920, height: 1080, surfaceId: this.surfaceId }).then(async (result: Array<scanBarcode.ScanResult>) => {// 处理扫码结果this.showScanResult(result);})this.isShowBack = false;}).visibility(this.isShowBack ? Visibility.Visible : Visibility.None)}Row() {Button('缩放比例,当前比例:' + this.setZoomValue).onClick(() => {// 设置相机缩放比例if (!this.isShowBack) {if (!this.zoomValue || this.zoomValue === this.setZoomValue) {this.setZoomValue = customScan.getZoom();} else {this.zoomValue = this.zoomValue;customScan.setZoom(this.zoomValue);setTimeout(() => {if (!this.isShowBack) {this.setZoomValue = customScan.getZoom();}}, 1000);}}})}.margin({ top: 10, bottom: 10 })Row() {TextInput({ placeholder: '输入缩放倍数' }).type(InputType.Number).borderWidth(1).backgroundColor(Color.White).onChange(value => {this.zoomValue = Number(value);})}}.width('50%').height(180)}}// 建议相机流设置为全屏.width('100%').height('100%').onClick((event: ClickEvent) => {if (this.isShowBack) {return;}let x1 = vp2px(event.displayY) / (this.displayHeight + 0.0);let y1 = 1.0 - (vp2px(event.displayX) / (this.displayWidth + 0.0));customScan.setFocusPoint({ x: x1, y: y1 });hilog.info(0x0001, this.TAG, `Succeeded in setting focusPoint x1: ${x1}, y1: ${y1}`);setTimeout(() => {customScan.resetFocus();}, 200);}).gesture(PinchGesture({ fingers: 2 }).onActionStart((event: GestureEvent) => {hilog.info(0x0001, this.TAG, 'Pinch start');}).onActionUpdate((event: GestureEvent) => {if (event) {this.scaleValue = event.scale;}}).onActionEnd((event: GestureEvent) => {try {let zoom = customScan.getZoom();this.pinchValue = this.scaleValue * zoom;customScan.setZoom(this.pinchValue);hilog.info(0x0001, this.TAG, 'Pinch end');} catch (error) {hilog.error(0x0001, this.TAG, `Failed to setZoom. Code: ${error.code}, message: ${error.message}`);}}))}
}

通过Callback方式回调,调用自定义界面扫码接口拉起相机流并返回扫码结果和相机预览流(YUV),具体可以参考Callback方式回调的示例代码。

了解更多详情>>

访问统一扫码服务联盟官网

获取自定义界面扫码服务开发指导文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/794394.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

zabbix 打通LDAP登录

` dn=dc=localdomain,dc=com 搜索属性=uid 绑定DN=cn=admin,dc=localdomain,dc=com`

tarjan—算法的神(一)

本篇包含 tarjan 求强连通分量、边双连通分量、割点 部分, tarjan 求点双连通分量、桥(割边)在下一篇。伟大的 Robert Tarjan 创造了众多被人们所熟知的算法及数据结构,最著名的如:(本文的)连通性相关的 tarjan 算法,Splay-Tree,Toptree,tarjan 求 lca 等等。 注:有…

gitlab代码恢复

gitlab代码恢复背景: 从gitlab的web端页面下载了代码。需要恢复到新的gitlab上。下面操作以vonebaas-c-fabric-explorer-front工程为例 已经下载好vonebaas-c-fabric-explorer-front工程的zip包:操作步骤 原来的git地址是:http://xxx.git.com:9002/ 新的git地址是:http://1…

Vidful.ai:免费在线AI视频生成器

免费在线AI视频生成器Vidful.ai,无需下载或复杂操作,轻松将文字和图片转换为高质量、电影级视频!免费在线AI视频生成器Vidful.ai,无需下载或复杂操作,轻松将文字和图片转换为高质量、电影级视频! 网址:https://vidful.ai/ 为什么选择Vidful.ai?免费在线平台:无需下载、…

“数据守护,商业共赢” — 华企盾招商会议圆满落幕

在数字化浪潮席卷全球的今天,数据安全已成为企业可持续发展的基石。为了共同探讨数据防护的新策略,推动行业生态的健康发展,我司于2024年9月6日成功举办了一场以“数据守护,商业共赢”为主题的招商会议。此次会议汇聚了来自各行各业的精英人士,共同探讨如何有效应对数据安…

【优技教育】Oracle 19c OCP 082题库(第16题)- 2024年修正版

【优技教育】Oracle 19c OCP 082题库(Q 16题)- 2024年修正版 考试科目:1Z0-082 考试题量:90 通过分数:60% 考试时间:150min 本文为(CUUG 原创)整理并解析,转发请注明出处,禁止抄袭及未经注明出处的转载。 原文地址:http://www.cuug.com/index.php?s=/home/article/deta…

Canvas绘制图片合成样式

效果图web * {margin: 0;padding: 0;}.container {position: relative;width: 328px;height: 328px;margin: 100px auto;}.container img {position: absolute;width: 328px;height: 328px;}#canvas {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);…

Axure的画图规范说明

Axure的画图规范说明自定义网页整体尺寸:1366x768;色值:# F8F9FD 顶部一级导航:1366x48;色值:# 1D8FFF 左右布局,左侧边导航:768x194;色值:# 191A23 侧边导航栏宽高:194x768 左侧一级菜单宽高:194x40/边距:16,字号:14,色值:#FFFFFF,图标:14x14,二级导航宽194w,…

解决vscode终端输出中文乱码问题图文教程

由于系统终端默认编码为GBK,所以需要修改为UTF-8 方法一 打开cmd输入chcp查看编码格式,查看以及修改如下图所示:方法二

Transformer、RNN和SSM的相似性探究:揭示看似不相关的LLM架构之间的联系

通过探索看似不相关的大语言模型(LLM)架构之间的潜在联系,我们可能为促进不同模型间的思想交流和提高整体效率开辟新的途径。 尽管Mamba等线性循环神经网络(RNN)和状态空间模型(SSM)近来备受关注,Transformer架构仍然是LLM的主要支柱。这种格局可能即将发生变化:像Jamba、Samba…