HarmonyOS NEXT Development Case: QR Code Generation and Recognition

This article demonstrates how to implement QR code generation and recognition in HarmonyOS NEXT using ArkUI components and system capabilities. The solution leverages multiple HarmonyOS Kits including ScanKit, MediaLibraryKit, and ImageKit to achieve core functionalities. Code Structure Overview import { componentSnapshot, // Component snapshot promptAction, // System prompt actions SegmentButton, // Segmented control component SegmentButtonItemTuple, // Type for segment items SegmentButtonOptions // Segmented control options } from '@kit.ArkUI'; // Import ArkUI component library import { photoAccessHelper } from '@kit.MediaLibraryKit'; // Media library access import { common } from '@kit.AbilityKit'; // Common abilities import { fileIo as fs } from '@kit.CoreFileKit'; // File system operations import { image } from '@kit.ImageKit'; // Image processing import { BusinessError, pasteboard } from '@kit.BasicServicesKit'; // Clipboard services import { hilog } from '@kit.PerformanceAnalysisKit'; // Logging system import { detectBarcode, scanBarcode } from '@kit.ScanKit'; // Barcode handling @Entry // Application entry @Component // Custom component struct QrCodeGeneratorAndScanner { // Component state management @State private buttonOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({ buttons: [{ text: 'Generate QR' }, { text: 'Scan QR' }] as SegmentButtonItemTuple, multiply: false, // Single selection fontColor: Color.White, selectedFontColor: Color.White, selectedBackgroundColor: Color.Orange, backgroundColor: "#d5d5d5", backgroundBlurStyle: BlurStyle.BACKGROUND_THICK }) @State private sampleText: string = 'hello world'; @State private inputText: string = ""; @State private scanResult: string = ""; @State @Watch('selectIndexChanged') selectIndex: number = 0 @State @Watch('selectedIndexesChanged') selectedIndexes: number[] = [0]; private qrCodeId: string = "qrCodeId" @State private scanResultObject: scanBarcode.ScanResult = ({} as scanBarcode.ScanResult) //... Other state variables // Event handlers selectedIndexesChanged() { this.selectIndex = this.selectedIndexes[0] } selectIndexChanged() { this.selectedIndexes[0] = this.selectIndex } private copyToClipboard(text: string): void { const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); pasteboard.getSystemPasteboard().setData(pasteboardData); promptAction.showToast({ message: 'Copied' }); } build() { Column() { SegmentButton({ options: this.buttonOptions, selectedIndexes: this.selectedIndexes }).width('400lpx').margin({ top: 20 }) Tabs({ index: this.selectIndex }) { // QR Generation Tab TabContent() { Scroll() { Column() { // Input section with sample/clear buttons QRCode(this.inputText) .width('300lpx') .aspectRatio(1) .id(this.qrCodeId) SaveButton().onClick(async () => { // Save QR to photo library const context = getContext(this) as common.UIAbilityContext; let helper = photoAccessHelper.getPhotoAccessHelper(context); //... Image capture and saving logic }) } } } // QR Scanning Tab TabContent() { Scroll() { Column() { // Camera scan and gallery picker buttons Row() { Text('Scan').onClick(() => { scanBarcode.startScanForResult(getContext(this), { enableMultiMode: true, enableAlbum: true }, (error, result) => { // Handle scan results }); }) Text('Gallery').onClick(() => { // Image selection and decoding logic detectBarcode.decode(inputImage, (error, result) => { // Handle decoding results }); }) } // Result display sections Text(`Result: ${this.scanResult}`) Text(JSON.stringify(this.scanResultObject)) } } } } } .backgroundColor("#f4f8fb"); } } Key Features Implementation 1. QR Code Generation Dynamic Input Handling: Uses TextArea with state management for real-time QR updates Image Export: componentSnapshot.get(this.qrCodeId).then((pixelMap) => { const imagePacker = image.createImagePacker(); imagePacker.packToFile(pixelMap, file.fd, { format: 'image/png', quality: 100 }); }); Media Library Integration: Leverages photoAccessHelper for secure storage 2. QR Code Recognition Camera Scanning: scanBarcode.startScanForResult(context,

May 11, 2025 - 05:00
 0
HarmonyOS NEXT Development Case: QR Code Generation and Recognition

Image description

This article demonstrates how to implement QR code generation and recognition in HarmonyOS NEXT using ArkUI components and system capabilities. The solution leverages multiple HarmonyOS Kits including ScanKit, MediaLibraryKit, and ImageKit to achieve core functionalities.

Code Structure Overview

import {
  componentSnapshot, // Component snapshot
  promptAction, // System prompt actions
  SegmentButton, // Segmented control component
  SegmentButtonItemTuple, // Type for segment items
  SegmentButtonOptions // Segmented control options
} from '@kit.ArkUI'; // Import ArkUI component library

import { photoAccessHelper } from '@kit.MediaLibraryKit'; // Media library access
import { common } from '@kit.AbilityKit'; // Common abilities
import { fileIo as fs } from '@kit.CoreFileKit'; // File system operations
import { image } from '@kit.ImageKit'; // Image processing
import { BusinessError, pasteboard } from '@kit.BasicServicesKit'; // Clipboard services
import { hilog } from '@kit.PerformanceAnalysisKit'; // Logging system
import { detectBarcode, scanBarcode } from '@kit.ScanKit'; // Barcode handling

@Entry // Application entry
@Component // Custom component
struct QrCodeGeneratorAndScanner {
  // Component state management
  @State private buttonOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({
    buttons: [{ text: 'Generate QR' }, { text: 'Scan QR' }] as SegmentButtonItemTuple,
    multiply: false, // Single selection
    fontColor: Color.White,
    selectedFontColor: Color.White,
    selectedBackgroundColor: Color.Orange,
    backgroundColor: "#d5d5d5",
    backgroundBlurStyle: BlurStyle.BACKGROUND_THICK
  })
  @State private sampleText: string = 'hello world';
  @State private inputText: string = "";
  @State private scanResult: string = "";
  @State @Watch('selectIndexChanged') selectIndex: number = 0
  @State @Watch('selectedIndexesChanged') selectedIndexes: number[] = [0];
  private qrCodeId: string = "qrCodeId"
  @State private scanResultObject: scanBarcode.ScanResult = ({} as scanBarcode.ScanResult)
  //... Other state variables

  // Event handlers
  selectedIndexesChanged() {
    this.selectIndex = this.selectedIndexes[0]
  }

  selectIndexChanged() {
    this.selectedIndexes[0] = this.selectIndex
  }

  private copyToClipboard(text: string): void {
    const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
    pasteboard.getSystemPasteboard().setData(pasteboardData);
    promptAction.showToast({ message: 'Copied' });
  }

  build() {
    Column() {
      SegmentButton({
        options: this.buttonOptions,
        selectedIndexes: this.selectedIndexes
      }).width('400lpx').margin({ top: 20 })

      Tabs({ index: this.selectIndex }) {
        // QR Generation Tab
        TabContent() {
          Scroll() {
            Column() {
              // Input section with sample/clear buttons
              QRCode(this.inputText)
                .width('300lpx')
                .aspectRatio(1)
                .id(this.qrCodeId)

              SaveButton().onClick(async () => {
                // Save QR to photo library
                const context = getContext(this) as common.UIAbilityContext;
                let helper = photoAccessHelper.getPhotoAccessHelper(context);
                //... Image capture and saving logic
              })
            }
          }
        }

        // QR Scanning Tab
        TabContent() {
          Scroll() {
            Column() {
              // Camera scan and gallery picker buttons
              Row() {
                Text('Scan').onClick(() => {
                  scanBarcode.startScanForResult(getContext(this), {
                    enableMultiMode: true,
                    enableAlbum: true
                  }, (error, result) => {
                    // Handle scan results
                  });
                })

                Text('Gallery').onClick(() => {
                  // Image selection and decoding logic
                  detectBarcode.decode(inputImage, (error, result) => {
                    // Handle decoding results
                  });
                })
              }

              // Result display sections
              Text(`Result: ${this.scanResult}`)
              Text(JSON.stringify(this.scanResultObject))
            }
          }
        }
      }
    }
    .backgroundColor("#f4f8fb");
  }
}

Key Features Implementation

1. QR Code Generation

  • Dynamic Input Handling: Uses TextArea with state management for real-time QR updates
  • Image Export:
  componentSnapshot.get(this.qrCodeId).then((pixelMap) => {
    const imagePacker = image.createImagePacker();
    imagePacker.packToFile(pixelMap, file.fd, {
      format: 'image/png',
      quality: 100
    });
  });
  • Media Library Integration: Leverages photoAccessHelper for secure storage

2. QR Code Recognition

  • Camera Scanning:
  scanBarcode.startScanForResult(context, {
    enableMultiMode: true,
    enableAlbum: true
  }, (error, result) => {
    // Error handling and result processing
  });
  • Image Decoding:
  detectBarcode.decode({ uri: imageUri }, (error, results) => {
    if (results.length > 0) {
      this.scanResult = results[0].originalValue;
    }
  });

Best Practices

  1. Permission Handling: Implement proper permission requests for camera and storage access
  2. Error Logging: Use hilog for detailed error tracking
  3. Performance Optimization:
    • Use component visibility control instead of conditional rendering
    • Implement proper resource cleanup in finally blocks
  4. User Experience:
    • Provide visual feedback with click effects
    • Use system clipboard for data sharing
    • Implement loading states for long operations

Conclusion

This implementation demonstrates HarmonyOS NEXT's powerful capabilities in handling modern mobile development requirements. The solution combines:

  • ArkUI's responsive UI components
  • ScanKit's robust scanning features
  • MediaLibraryKit's secure storage access
  • ImageKit's efficient processing capabilities

Developers can extend this foundation with additional features like batch processing, history tracking, or custom QR code styling based on specific application requirements.