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,

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
- Permission Handling: Implement proper permission requests for camera and storage access
-
Error Logging: Use
hilog
for detailed error tracking -
Performance Optimization:
- Use component visibility control instead of conditional rendering
- Implement proper resource cleanup in finally blocks
-
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.