鸿蒙自定义二维码扫描页面
前言
公司的鸿蒙APP功能还不多,最近把想写的一些内容也写的差不多了,还剩一两篇吧,写的内容都是我觉得有点意思,值得一写的,其他常用功能按照官方demo也能做个七七八八。 最近还是打算跳槽了,把最后两篇鸿蒙文章写完,全身心投入吧,不知道行情怎么样。
这篇文章是鸿蒙自定义的二维码扫描页面,官方的扫码功能其实已经不错了,但是产品经理想iOS、Android、鸿蒙一致,没得办法只能做一下了。
效果图
先贴一个效果图吧,觉得可以再看下面代码喽!
官方二维码扫描
先来看下官方的二维码扫描:
qrScan(): Promise<string> {
let options: scanBarcode.ScanOptions = {
scanTypes: [scanCore.ScanType.ALL],
enableMultiMode: false,
enableAlbum: true
};
return scanBarcode.startScanForResult(this.context, options)
.then((result: scanBarcode.ScanResult) => {
LogUtil.d('startScanForResult: ' + result.originalValue);
return result.originalValue;
}).catch((error: BusinessError) => {
let result = `error ${error.code}: ${error.message}`
LogUtil.e(result);
return result;
})
}
支持多种类型,支持相机和相册的二维码扫描,不要求自定义UI的话,选它就可以了。
官方文档
在自定义扫描界面前,还是看下官方文档吧,虽然很简单,但是可以照着改:
如果要用router来传参或者返回的话,还可以看下这个:
自定义扫描页面
下面开始自定义扫码页面,我直接在官方扫码界面上改,加上几点功能,完整代码后面再贴。
添加相册扫码
官方的扫码只支持相机,并不支持相册二维码,这个也简单,加个图片控件,点击通过相册去选图片,再交给detectBarcode识别,识别后丢到相机显示结果的逻辑:
Image($r('app.media.select_from_album'))
.width(20)
.height(20)
.onClick(() => {
let options: scanBarcode.ScanOptions = {
scanTypes: [scanCore.ScanType.ALL],
enableMultiMode: true,
enableAlbum: true
}
try {
let photoOption = new picker.PhotoSelectOptions();
photoOption.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoOption.maxSelectNumber = 1;
let photoPicker = new picker.PhotoViewPicker();
photoPicker.select(photoOption).then((result) => {
try {
let inputImage: detectBarcode.InputImage = { uri: result.photoUris[0] }
detectBarcode.decode(inputImage, options).then((result: Array ) => {
this.showScanResult(result);
hilog.info(0x0001, '[Scan Sample]', 'Promise scan result: %{public}s', JSON.stringify(result));
}).catch((failResult: BusinessError) => {
hilog.error(0x0001, '[Scan Sample]', 'Promise Error: %{public}s', JSON.stringify(failResult));
});
} catch (error) {
hilog.error(0x0001, '[Scan Sample]', 'Catch Error: failResult: %{public}s', JSON.stringify(error));
}
})
} catch (error) {
hilog.error(0x0001, '[Scan Sample]', 'error: %{public}s', JSON.stringify(error));
}
})
增加二维码扫描动画
按理来说这里可以用canvas来实现的,不过不想搞那么复杂,凑合用吧,用了几个控件加个计时器做了个:
RelativeContainer() {
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
.id('scanArea')
Image($r('app.media.scan_line')).width('100%')
.margin({top: this.scanBarOffset})
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
}
.width(250)
.height(250)
.margin({top: 150})
.alignRules({
middle: { anchor: '__container__', align: HorizontalAlign.Center },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
直接四个Row四个Column围出一个正方形四个角,中间放一个扫描图片,然后加个计时器修改它的marginTop就可以了:
async onPageShow() {
this.startScanBar();
}
private timer: number | null = null;
private count = 0;
startScanBar() {
if (this.timer) {
clearInterval(this.timer);
}
this.count = 0
this.timer = setInterval(()=>{
this.count++;
this.count = this.count % 500;
if (this.count < 250) {
this.scanBarOffset = this.count;
}else {
this.scanBarOffset = 500 - this.count;
}
}, 10);
}
完整代码
和官方案例大差不差,就加了上面两样东西,返回我这直接设置了个静态回调处理,不推荐这么写,可以通过router去拿结果:
import { customScan, detectBarcode, scanBarcode, scanCore } from '@kit.ScanKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { abilityAccessCtrl, common } from '@kit.AbilityKit'
import { router } from '@kit.ArkUI'
import { picker } from '@kit.CoreFileKit'
@Entry
@Component
struct CustomScanPage {
@State userGrant: boolean = false
@State surfaceId: string = ''
@State cameraHeight: number = 640
@State cameraWidth: number = 360
@State scanBarOffset: number = 0
private mXComponentController: XComponentController = new XComponentController()
private TAG: string = '[customScanPage]'
async showScanResult(result: Array ) {
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!');
}).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));
}
router.back();
}
}
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));
}
this.startScanBar();
}
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!');
}).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));
}
}
build() {
Stack() {
if (this.userGrant) {
Column() {
XComponent({
id: 'componentId',
type: 'surface',
controller: this.mXComponentController
})
.onLoad(async () => {
hilog.info(0x0001, this.TAG, 'onLoad is called');
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 {
customScan.start(viewControl)
.then(async (result: ArrayScanResult>) => {
this.showScanResult(result);
})
} catch (error) {
hilog.error(0x0001, this.TAG, 'start fail, error: %{public}s ', JSON.stringify(error));
}
})
.width(this.cameraWidth)
.height(this.cameraHeight)
.position({ x: 0, y: 0 })
}
.height('100%')
.width('100%')
}
RelativeContainer() {
Image($r('app.media.video_back'))
.width(30)
.height(30)
.margin({ top: 50, left: 20 })
.padding(6)
.borderRadius(15)
.backgroundColor("#6666")
.onClick(() => {
router.back();
})
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
RelativeContainer() {
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
Row().width(20).height(5).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
Column().width(5).height(20).backgroundColor("#ffc66f23")
.alignRules({
right: { anchor: '__container__', align: HorizontalAlign.End },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
})
.id('scanArea')
Image($r('app.media.scan_line')).width('100%')
.margin({top: this.scanBarOffset})
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Row()
.width(40)
.height(40)
.backgroundColor('#20FFFFFF')
.borderRadius(20)
.margin({ top: 40 })
.alignRules({
middle: { anchor: '__container__', align: HorizontalAlign.Center },
top: { anchor: 'scanArea', align: VerticalAlign.Bottom }
})
Image($r('app.media.select_from_album'))
.width(20)
.height(20)
.margin({top:50})
.alignRules({
middle: { anchor: '__container__', align: HorizontalAlign.Center },
top: { anchor: 'tipTwo', align: VerticalAlign.Bottom }
})
.onClick(() => {
let options: scanBarcode.ScanOptions = {
scanTypes: [scanCore.ScanType.ALL],
enableMultiMode: true,
enableAlbum: true
}
try {
let photoOption = new picker.PhotoSelectOptions();
photoOption.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoOption.maxSelectNumber = 1;
let photoPicker = new picker.PhotoViewPicker();
photoPicker.select(photoOption).then((result) => {
try {
let inputImage: detectBarcode.InputImage = { uri: result.photoUris[0] }
detectBarcode.decode(inputImage, options).then((result: Array ) => {
this.showScanResult(result);
hilog.info(0x0001, '[Scan Sample]', 'Promise scan result: %{public}s', JSON.stringify(result));
}).catch((failResult: BusinessError) => {
hilog.error(0x0001, '[Scan Sample]', 'Promise Error: %{public}s', JSON.stringify(failResult));
});
} catch (error) {
hilog.error(0x0001, '[Scan Sample]', 'Catch Error: failResult: %{public}s', JSON.stringify(error));
}
})
} catch (error) {
hilog.error(0x0001, '[Scan Sample]', 'error: %{public}s', JSON.stringify(error));
}
})
}
.width(250)
.height(250)
.margin({top: 150})
.alignRules({
middle: { anchor: '__container__', align: HorizontalAlign.Center },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
}
private timer: number | null = null;
private count = 0;
startScanBar() {
if (this.timer) {
clearInterval(this.timer);
}
this.count = 0
this.timer = setInterval(()=>{
this.count++;
this.count = this.count % 500;
if (this.count < 250) {
this.scanBarOffset = this.count;
}else {
this.scanBarOffset = 500 - this.count;
}
}, 10);
}
}
小结
根据官方自定义二维码扫描页面修改,增加了相册图片扫描,和一个方框扫描动画,用起来还可以!
阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22347,转载请注明出处。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22347,转载请注明出处。
评论0