自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

OpenHarmony應用實現(xiàn)二維碼掃碼識別

系統(tǒng) OpenHarmony
本文就以橘子購物示例應用為例,來講解OpenHarmony應用二維碼開發(fā)相關的技術點。

想了解更多關于開源的內容,請訪問:

51CTO 開源基礎軟件社區(qū)

https://ost.51cto.com

概念介紹

二維碼的應用場景非常廣泛,在購物應用中,消費者可以直接掃描商品二維碼,瀏覽并購買產品,如圖是購物應用的掃描二維碼的頁面。

本文就以橘子購物示例應用為例,來講解OpenHarmony應用二維碼開發(fā)相關的技術點。

我們先看下二維碼相關的幾個概念。

  • 二維碼生成

OpenHarmony應用框架提供了QRCode組件,用于顯示單個二維碼的組件。該組件只能用于顯示二維碼,無法顯示條碼與解析碼內容。

  • 二維碼解析

OpenHarmony提供了功能強大的三方庫 @ohos/zxing,是一個解析/生成一維碼/二維碼的庫。詳細內容可以參考@ohos/zxing。

二維碼解析時,通常有兩種方式,使用相機拍攝獲取圖片或打開相冊選取圖片,然后圖片解析合適的圖片格式,進行二維碼解析。

橘子購物示例應用掃描二維碼的示例圖:

OpenHarmony應用實現(xiàn)二維碼掃碼識別-開源基礎軟件社區(qū)OpenHarmony應用實現(xiàn)二維碼掃碼識別-開源基礎軟件社區(qū)

配置文件

了解了二維碼相關的概念后,我們看下橘子購物示例應用的oh-package.json5配置文件。

在橘子購物示例應用中,實現(xiàn)首頁二維碼掃描的頁面的文件位置為:entry/src/main/ets/pages/ScanPage.ets。文件內容如下:

import { QRCodeScanComponent } from "@ohos/scan-component"
@Entry
@Component
struct Scan {
  build() {
    Column() {
      QRCodeScanComponent()
    }
  }
}

內容非常簡單,主要是導入的自定義組件QRCodeScanComponent,這個組件的代碼來自:二維碼掃描示例應用,后文我們這樣分析如何開發(fā)這個二維碼掃描應用。

從這一行,可以了解到OpenHarmony應用如何引用ohpm本地三方庫。

"@ohos/scan-component": "file:../libs/ohos-qr-code-scan-1.0.1.har",

oh-package.json5配置文件片段如下:

{
  "license": "ISC",
  "devDependencies": {},
  "name": "product",
  "description": "example description",
  "repository": {},
  "version": "1.0.0",
  "dependencies": {
    "@ohos/http": "file:../libs/ohos-http-1.0.0.tgz",
    "@ohos/video-component": "file:../libs/ohos-video-component-1.0.5.tgz",
    "@ohos/details-page-component": "file:../feature/detailPage",
    "@ohos/notification": "file:../libs/ohos-notification-1.0.0.tgz",
    "@ohos/scan-component": "file:../libs/ohos-qr-code-scan-1.0.1.har",
    "@ohos/updatedialog": "file:../libs/ohos-updatedialog-1.0.0.tgz",
    "@ohos/enter-animation": "file:../libs/ohos-enter-animation-1.0.1.tgz",
    "@ohos/share-component": "file:../libs/ohos-sharecomponent-1.0.1.tgz",
    "@ohos/emitter": "file:../feature/emitter",
    "@ohos/navigation-component": "file:../feature/navigationHome"
  }
}

開發(fā)步驟

我們來看二維碼掃描功能是如何開發(fā)的。

導入ohpm三方庫

在開發(fā)前,我們需要導入ohpm組件庫:@ohos/zxing??梢允褂妹钚蟹绞綄雘hpm install @ohos/zxing,也可以直接在文件entry\oh-package.json5中配置,如文件片段所示。

可以看出,二維碼掃描的核心代碼存放在Feature目錄,是一個獨立的module模塊,方便復用:

“@ohos/feature-qr-code-scan”: “file:…/Feature”。

文件entry\oh-package.json5片段:

"dependencies": {
    "@ohos/feature-qr-code-scan": "file:../Feature",
    "@ohos/zxing": "^2.0.0"
  }

相機服務

CameraService.ets文件相機服務構造函數中,會創(chuàng)建一個圖片接收器。

該圖片接收器可以監(jiān)聽’imageArrival’事件,當相機拍照時會觸發(fā)該事件。在監(jiān)聽事件的回調函數里,實現(xiàn)對拍照的圖片進行處理。

CameraService.ets文件相機服務構造函數:

constructor(imgReceiver?: image.ImageReceiver) {
    if (imgReceiver === undefined) {
      this.imageReceiver = image.createImageReceiver(QRCodeScanConst.IMG_DEFAULT_SIZE.WIDTH,
      QRCodeScanConst.IMG_DEFAULT_SIZE.HEIGHT, image.ImageFormat.JPEG, QRCodeScanConst.MAX_IMAGE_CAPACITY)
    } else {
      this.imageReceiver = image.createImageReceiver(imgReceiver.size.width, imgReceiver.size.height,
      imgReceiver.format, imgReceiver.capacity)
    }
  }

在CameraService.ets文件創(chuàng)建相機函數中,主要包含如下幾個步驟:

  • 獲取支持的相機

根據context獲取CameraManager,然后獲取支持的相機(攝像頭)。如果沒有支持的相機,則然后。

如有支持的相機,則默認使用相機列表中的第一個。實際應用中,對于二維碼掃描,需要使用后置相機攝像頭。

  • 獲取相機輸入輸出流

首先,根據指定的相機,創(chuàng)建相機輸入流this.cameraInput。

然后,獲取相機的cameraOutputCapability參數,接著創(chuàng)建兩個輸出流:

  • 預覽輸出流

創(chuàng)建相機預覽輸出流this.previewOutput,使用的surfaceId來自XComponent組件。預覽輸出流,對應相機拍照前的圖片預覽。

  • 相片輸出流

創(chuàng)建相片輸出流this.photoOutput,使用的receivingSurfaceId來自上文創(chuàng)建的圖片接收器。相片輸出流,用于保存到相片。

  • 配置相機會話

配置相機會話,也比較簡單,添加輸入流和輸出流即可,見代碼及其注釋。

CameraService.ets文件創(chuàng)建相機函數:

/**
   * 創(chuàng)建相機
   */
  async createCamera(surfaceId: string) {
    Logger.info("createCamera start")
    // 根據context獲取CameraManager
    let cameraManager = camera.getCameraManager(AppStorage.Get('context'))
    // 獲取Camera對象數組
    let cameras = cameraManager.getSupportedCameras()
    // 沒有相機就停止
    if (cameras.length === 0) {
      Logger.error("createCamera: cameras length is 0.")
      return
    }
    // 拿到相機列表中的第一個默認相機id, 根據id獲取相機輸入流
    this.cameraInput = cameraManager.createCameraInput(cameras[0])
    this.cameraInput.open()
    // 獲取cameraOutputCapability參數
    let cameraOutputCapability = cameraManager.getSupportedOutputCapability(cameras[0])
    // 獲取相機輸出流
    this.previewOutput = cameraManager.createPreviewOutput(cameraOutputCapability.previewProfiles[0], surfaceId)
    // 獲取一個可以創(chuàng)建相片輸出流的id
    let receivingSurfaceId = await this.imageReceiver.getReceivingSurfaceId()
    // 創(chuàng)建相片輸出流
    this.photoOutput = cameraManager.createPhotoOutput(cameraOutputCapability.photoProfiles[0], receivingSurfaceId)
    // 獲取捕獲會話的實例
    this.captureSession = cameraManager.createCaptureSession()
    // 開始會話配置
    this.captureSession.beginConfig()
    // 使用相機輸入流---添加一個攝像頭輸入流
    this.captureSession.addInput(this.cameraInput)
    // 使用相機輸出流---添加一個攝像頭輸出
     this.captureSession.addOutput(this.previewOutput)
    // 使用相片輸出流---添加相機照片的輸出
    this.captureSession.addOutput(this.photoOutput)
    // 結束并提交配置
    await this.captureSession.commitConfig()
    // 開始捕獲會話
    await this.captureSession.start()
    Logger.info("createCamera end")
  }

CameraService.ets文件拍照函數中,指定相片參數設置,然后調用capture()函數完成拍照。

拍照后會觸發(fā)圖片接收器的’imageArrival’事件。拍照函數在使用相機掃描二維碼的時候調用。

該圖片接收器可以監(jiān)聽’imageArrival’事件,當相機拍照時會觸發(fā)該事件。在監(jiān)聽事件的回調函數里,實現(xiàn)對拍照的圖片進行處理。

CameraService.ets文件拍照函數:

takePicture() {
    let photoSetting = {
      rotation: camera.ImageRotation.ROTATION_0,
      quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
      mirror: false
    }
    this.photoOutput.capture(photoSetting)
  }

二維碼解析實現(xiàn)代碼

二維碼解析類文件為:QRCodeParser.ets,支持拍照識別二維碼,還支持從相冊選擇二維碼圖片進行識別。

我們首先看下如何解析從相機獲取的二維碼圖片,對應函數為:parseQRCodeImageFromCamera,該類指定一個時間隨機的圖片文件名,圖片歸檔格式,然后繼續(xù)調用函數parseQRCodeImageWithNameFromCamera。

/**
   * 解析從相機獲取的二維碼圖片
   *
   * @param cameraService
   * @param canvasContext
   */
  parseQRCodeImageFromCamera(cameraService: CameraService,
                             imageComponentType?: image.ComponentType): void {
    Logger.info("parseQRCodeImageFromCamera start")
    let fileName = this.getRandomFileName(QRCodeScanConst.IMG_FILE_PREFIX, QRCodeScanConst.IMG_SUFFIX_JPG)
    this.parseQRCodeImageWithNameFromCamera(cameraService, fileName, imageComponentType);
    Logger.info("parseQRCodeImageFromCamera end")
  }

在函數parseQRCodeImageWithNameFromCamera中,注冊圖片接收器監(jiān)聽’imageArrival’事件,在監(jiān)聽函數里,對二維碼圖片進行解析識別。

當相機對二維碼拍照后,二維碼圖片會被保存到指定的目錄下,返回文件URI。保存圖片的函數createPublicDirFileAsset的實現(xiàn),可以自行查閱源碼。

根據返回的圖片URI,調用函數parseImageQRCode對二維碼進行解析。函數parseImageQRCode后文會介紹。

如果解析失敗,彈窗提示解析失敗。如果解析成功,會被解析結果保存到AppStorage。

保存到AppStorage的二維碼解析結果會被@watch裝飾器的變量監(jiān)視,當監(jiān)視到有二維碼識別結果后,會在界面展示,后文會介紹。

QRCodeParser.ets文件parseQRCodeImageWithNameFromCamera函數代碼:

/**
   * 解析從相機獲取的二維碼圖片,指定文件名稱
   *
   * @param cameraService
   * @param canvasContext
   */
  parseQRCodeImageWithNameFromCamera(cameraService: CameraService,
                                     fileDisplayName: string,
                                     imageComponentType?: image.ComponentType): void {
    Logger.info("parseQRCodeImageWithNameFromCamera...")
    cameraService.imageReceiver.on('imageArrival', async () => {
      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival start")
      // 從接收器獲取下一個圖像,并返回結果
      let targetImage: image.Image = await cameraService.imageReceiver.readNextImage()
      // 默認按JPEG格式處理
      let imgComponentType = imageComponentType === undefined ? image.ComponentType.JPEG : imageComponentType
      let imageComponent = await targetImage.getComponent(imgComponentType)
      // 將image的ArrayBuffer寫入指定文件中,返回文件uri
      let imageUri = await this.createPublicDirFileAsset(fileDisplayName, mediaLibrary.MediaType.IMAGE,
                     mediaLibrary.DirectoryType.DIR_IMAGE, imageComponent.byteBuffer);
      // 釋放已讀取的image資源,以便處理下一個資源
      await targetImage.release()

      // 解析二維碼
      let qrCodeParseRlt = await this.parseImageQRCode(imageUri);
      if (!qrCodeParseRlt.isSucess) {
        Logger.error("parseQRCodeImageWithNameFromCamera qrCodeParseRlt is null")
        prompt.showToast({
          message: $r('app.string.qrCodeNotRecognized')
        })
        return;
      }
      // 拼接解析結果
      AppStorage.SetOrCreate(QRCodeScanConst.QR_CODE_PARSE_RESULT, qrCodeParseRlt.decodeResult);
      Logger.info("parseQRCodeImageWithNameFromCamera imageArrival end")
    })
  }

二維碼解析類文件為:QRCodeParser.ets,支持拍照識別二維碼,還支持從相冊選擇二維碼圖片進行識別。

我們接著,再看下如何解析從相冊里挑選的二維碼圖片。

參數imageSrc為選定圖片的URI地址。

getImageSource()代碼可以自行查詢,實現(xiàn)根據圖片URI返回圖片的寬、高,以及圖片的pixelMap數據。然后,把像素數據寫入ArrayBuffer,供zxing二維碼識別程序使用。

函數RGBLuminanceSource、BinaryBitmap、BinaryBitmap等都是zxing的類。通過調用MultiFormatReader的decode函數對二維碼圖像進行解析。

如果解析成功,會返回成功的標記和解析的結果。

如果解析失敗,會在catch語句塊里進行處理,會返回失敗的標記和解析失敗的原因。

QRCodeParser.ets文件parseImageQRCode函數代碼:

/**
   * 解析圖片二維碼信息
   * @param canvasContext
   * @param imageSrc
   */
  async parseImageQRCode(imageSrc: string): Promise<DecodeResultAttribute> {
    Logger.info(`parseImageQRCode start`);
    // 獲取圖片的寬高
    let imageSource = await this.getImageSource(imageSrc);
    let imageWidth = imageSource.width;
    let imageHeight = imageSource.height;
    // 獲取PixelMap圖片數據
    let pixMapData = imageSource.pixelMap;
    let pixelBytesNumber = pixMapData.getPixelBytesNumber();
    let arrayBuffer: ArrayBuffer = new ArrayBuffer(pixelBytesNumber);
    // 讀取圖像像素數據,結果寫入ArrayBuffer里
    await pixMapData.readPixelsToBuffer(arrayBuffer);
    let int32Array = new Int32Array(arrayBuffer);
    let luminanceSource = new RGBLuminanceSource(int32Array, imageWidth, imageHeight);
    let binaryBitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
    let mltiFormatReader = new MultiFormatReader();
    let hints = new Map();
    hints.set(DecodeHintType.POSSIBLE_FORMATS, [BarcodeFormat.QR_CODE]);
    mltiFormatReader.setHints(hints);
    try {
      // 解析二維碼
      let decodeResult = mltiFormatReader.decode(binaryBitmap);
      let decodeText = decodeResult.getText();
      Logger.info(`parseImageQRCode end ${decodeText}`);
      return { isSucess: true, decodeResult: decodeText };
    } catch (err) {
      let error = `The error is ${err}`;
      Logger.info(`parseImageQRCode end`);
      return { isSucess: false, decodeResult: error };
    }
  }

相機掃描識別二維碼

在文件QRCodeScanComponent.ets中實現(xiàn)了二維碼掃描自定義組件。我們看下該文件中如何實現(xiàn)相機掃描二維碼的。

在二維碼掃描組件的aboutToAppear()函數調用的watchCameraPermission()函數,用于使用相機掃描二維碼進行識別。

在watchCameraPermission()函數中,使用setInterval函數每100ms判斷下是否具有相機權限,當有相機權限的時候,才能使用相機掃描二維碼。

當具備相機權限時 ,使用setInterval函數每4000ms輪詢判斷下是否識別到二維碼圖片,如果識別到則取消執(zhí)行輪詢。

如果沒有識別到二維碼,則繼續(xù)調用函數takePicture()拍照。調用該函數后,會觸發(fā)圖片接收器的監(jiān)聽事件’imageArrival’,對這個事件的監(jiān)聽分析,見上文。

文件QRCodeScanComponent.ets中,相機拍照識別二維碼的代碼片段:

aboutToAppear() {
// 監(jiān)聽相機權限
this.watchCameraPermission()
// 設置掃描動畫
this.setQRCodeScanAnimation()
// 解析二維碼圖片信息
this.qrCodeParser.parseQRCodeImageFromCamera(this.cameraService);
}
......
// 監(jiān)聽相機權限變化
watchCameraPermission() {
let interval = setInterval(() => {
  this.hasCameraPermission = AppStorage.Get(QRCodeScanConst.HAS_CAMERA_PERMISSION)
  if (this.hasCameraPermission) {
	let qrCodeScanInterval = setInterval(() => {
	  if (this.qrCodeParseResult.length > 0 || this.isQRCodeScanStopped) {
		clearInterval(qrCodeScanInterval)
	  }
	  // 拍照
	  this.cameraService.takePicture()
	}, 4000)
	clearInterval(interval)
  }
}, 100)
}

識別相冊二維碼圖片

在文件QRCodeScanComponent.ets中實現(xiàn)了二維碼掃描自定義組件。我們看下該文件中如何識別相冊二維碼圖片。

首先,設置this.isQRCodeScanStopped為true,這個會關閉相機拍照識別二維碼。

然后,通過startAbilityForResult啟動相冊應用,供用戶選擇二維碼圖片。

如果選擇圖片失敗,則彈窗報錯。

如果選擇圖片成功,則調用二維碼解碼函數parseImageQRCode完成對圖片二維碼的識別。

如果識別二維碼成功,則彈窗展示二維碼結果。

如果識別識別,則toast展示:未識別到二維碼。

文件QRCodeScanComponent.ets中,相冊選擇二維碼圖片進行識別代碼片段:

Image($r('app.media.scan_photo'))
  .width(30)
  .height(30)
  .id('scanPhoto')
  .onClick(async () => {
	// 打開相冊獲取圖片
	this.isQRCodeScanStopped = true
	let context = AppStorage.Get('context') as common.UIAbilityContext
	await context.startAbilityForResult({
	  parameters: { uri: 'singleselect' },
	  bundleName: 'com.ohos.photos',
	  abilityName: 'com.ohos.photos.MainAbility',
	}).then(data => {
	  // 獲取want數據
	  let want = data['want'];
	  if (want) {
		// param代表want參數中的paramters
		let param = want['parameters'];
		if (param) {
		  // 被選中的圖片路徑media/image/8
		  let selectedUri = param['select-item-list'];
		  setTimeout(async () => {
			if (!selectedUri) {
			  prompt.showToast({
				message: $r('app.string.queryImageFailed'),
				duration: 1000
			  })
			  return;
			}
			// 獲取解析數據
			let qrCodeParseRlt = await this.qrCodeParser.parseImageQRCode(selectedUri[0]);
			if (qrCodeParseRlt.isSucess) {
			  prompt.showDialog({
				title: $r('app.string.qrcodeResult'),
				message: qrCodeParseRlt.decodeResult
			  })
			} else {
			  prompt.showToast({
				message: $r('app.string.qrCodeNotRecognized')
			  })
			}
		  }, 50)
		}
	  }
	})
  })

二維碼掃描組件界面

在文件QRCodeScanComponent.ets中實現(xiàn)了二維碼掃描自定義組件。我們看下二維碼掃描組件的頁面布局。

整個頁面使用Stack進行堆疊布局。

如果有相機權限,會XComponent組件,用于展示相機的預覽輸出流。XComponent組件的onLoad函數里會創(chuàng)建相機,onDestroy函數里會釋放相機。

Image($r('app.media.scan_border'))圖片就是二維碼掃描框,引導用戶把二維碼放到框內進行掃描識別。

Divider是個分割線,該分割線使能了動畫效果,在識別二維碼的過程中,分割線從二維碼識別框里從上到下移動。掃描動畫實現(xiàn)代碼如下:

// 掃描掃描動畫
  setQRCodeScanAnimation() {
    setInterval(() => {
      animateTo({
        duration: 1000, // 動畫時間
        tempo: 0.5, // 動畫速率
        curve: Curve.EaseInOut,
        delay: 200, // 動畫延遲時間
        iterations: -1, // 動畫是否重復播放
        playMode: PlayMode.Normal,
      }, () => {
        this.animationOrdinate = 390 // 掃描動畫結束Y坐標
      })
    }, 2000)
  }

Text($r('app.string.putTheQRCodeToScan'))引導用戶把二維碼放到框內進行掃描識別。

Image($r('app.media.scan_back'))返回退出應用。

Image($r('app.media.scan_photo'))從相冊里挑選二維碼圖片進行識別。

build() {
Column() {
  Stack() {
	if (this.hasCameraPermission) {
	  XComponent({
		id: 'componentId',
		type: 'surface',
		controller: this.xComponentController
	  })
		.onLoad(() => {
		  // 適配可能需要獲取設備信息
		  this.xComponentController.setXComponentSurfaceSize({
			surfaceWidth: QRCodeScanConst.IMG_DEFAULT_SIZE.WIDTH,
			surfaceHeight: QRCodeScanConst.IMG_DEFAULT_SIZE.HEIGHT
		  })
		  this.surFaceId = this.xComponentController.getXComponentSurfaceId()
		  this.cameraService.createCamera(this.surFaceId)
		})
		.onDestroy(() => {
		  this.cameraService.releaseCamera()
		})
		.height('100%')
		.width('100%')
	}
	Column() {
	  Column() {
		Image($r('app.media.scan_border'))
		......
		Divider()
		  .strokeWidth(1)
		  .height(4)
		  .width('100%')
		  .color(Color.White)
		  .width('100%')
		  .position({ x: 0, y: 0 })
		  .translate({ x: 0, y: this.animationOrdinate })
	  }
	......
	  Text($r('app.string.putTheQRCodeToScan'))
	......
	}
	......
	Row() {
	  Image($r('app.media.scan_back'))
	  ......
	  Row({ space: 16 }) {
		Image($r('app.media.scan_photo'))
		......
}

運行測試效果

可以下載橘子購物示例應用代碼,使用DevEco Studio編譯構建,使用Simulator模擬器或者真實設備進行運行體驗??梢泽w驗下使用相機對二維碼圖片進行識別,還可以嘗試下識別相冊中的二維碼圖片。

git init
git config core.sparsecheckout true
echo code/Solutions/Shopping/OrangeShopping/ > .git/info/sparse-checkout
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
git pull origin master

注意事項

當前二維碼示例應用識別相冊的二維碼,彈出識別結果后,程序會崩潰,已經提單跟蹤。示例程序待改進。

使用相機功能直接拍攝二維碼的功能,一直沒有成功運行,需要進一步優(yōu)化。

想了解更多關于開源的內容,請訪問:

51CTO 開源基礎軟件社區(qū)

https://ost.51cto.com

責任編輯:jianghua 來源: 51CTO 開源基礎軟件社區(qū)
相關推薦

2024-06-26 08:46:45

2023-11-17 09:07:51

.NET生成二維碼識別二維碼

2023-11-17 15:44:06

C++庫生成二維碼

2011-12-06 16:40:45

二維碼快拍二維碼靈動快拍

2013-01-30 12:16:49

二維碼NFC近場通訊

2013-12-03 10:32:52

2021-09-08 10:02:56

面試二維碼前端

2024-03-07 07:59:37

2023-05-09 20:41:00

網絡詐騙網絡安全

2013-03-27 16:13:17

Android開發(fā)Android二維碼生QR生成

2013-11-19 16:06:39

2017-02-21 09:17:46

二維碼漏洞

2013-11-26 09:54:02

2018-03-05 18:39:25

2011-11-24 16:00:23

信息圖QR碼二維碼

2024-11-20 13:00:02

2021-04-09 15:55:07

掃碼支付二維碼數字人民幣

2023-12-25 14:53:36

2012-04-01 09:53:13

二維碼

2014-03-05 14:41:55

二維碼登錄
點贊
收藏

51CTO技術棧公眾號