Read the original article:Creating a Custom Camera UI in HarmonyOS Using Stack and XComponent
Need to scan a face, ID, credit card, or IBAN? Here’s how you can build a beautiful custom camera overlay in HarmonyOS using ArkTS and Huawei’s Camera Kit.
Introduction
In many mobile apps, a basic camera preview just isn’t enough. Whether you’re scanning an identity card, credit card, or performing face verification, users expect a clean and guided interface with visual cues.
Thanks to Huawei’s Camera Kit and ArkUI’s Stack + XComponent structure, we can build a camera screen with a custom frame overlay that feels intuitive and professional.
In this article, we’ll build a custom camera screen using Stack and XComponent, ideal for use cases like IBAN detection or OCR-based scanning.
Background: What Are Stack and XComponent?
- XComponent is the gateway to native components like the camera. It renders the camera preview on the screen.
- Stack, on the other hand, allows layered UI composition — meaning we can put overlays, text, buttons, or frames on top of the camera feed.
This combination gives us full visual control over the camera screen.
Use Case: Capture an IBAN Within a Custom Frame
In this example, we’ll show how to implement a screen that:
- Displays a camera feed
- Adds a dashed-border frame for scanning
- Places test instructions and a capture button below the camera view
- Provides a fully styled and interactive UI using ArkTS and ArkUI components
Implementation: ArkTS Layout with Camera Frame Overlay
Here’s the full layout code using ArkTS:
build() {
Row() {
Column() {
Stack() {
// CAMERA PREVIEW
XComponent({
id: CommonConstants.XCOMPONENT_ID,
type: XComponentType.SURFACE,
controller: this.xcomponentController
})
.onLoad(async () => {
await this.camera.releaseCamera();
this.XComponentinit()
})
.width(CommonConstants.FULL_WIDTH)
.height(this.xcomponentHeight)
// OVERLAY FRAME
if (this.showOverlay) {
Column() {
Row().height('25%').width('100%') // Top spacer
Column() {
Row() {
Row().width('2%') // Left margin
Column()
.width('96%')
.height('100%')
.border({
color: Color.Black,
width: '12px',
radius: '8px',
style: BorderStyle.Dashed
})
Row().width('2%') // Right margin
}
.height('95%') // Frame height
}
.width('100%')
.height('50%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Row().height('25%').width('100%') // Bottom spacer
}
.width('100%')
.height('100%')
}
}
.width(CommonConstants.FULL_WIDTH)
.height(this.xcomponentHeight)
.margin({ top: $r('app.float.top_height') })
// BOTTOM BUTTON + TEXT
Column() {
Text($r('app.string.Recognize_text'))
.fontSize($r('app.float.button_tip_size'))
.fontColor(Color.White)
.margin({ top: $r('app.float.indicate_upper_margin') })
// Decorative dot
Row()
.backgroundColor($r('app.color.round_color'))
.width($r('app.float.decorative_point_size'))
.height($r('app.float.decorative_point_size'))
.border({
radius: $r('app.float.decorative_dots_rounded_corners')
})
.margin({
top: $r('app.float.decorative_dots_upper_margin'),
bottom: $r('app.float.decorative_dots_bottom_margin')
})
// CAPTURE BUTTON
Row() {
Row()
.backgroundColor(Color.White)
.width($r('app.float.button_size'))
.height($r('app.float.button_size'))
.border({
radius: $r('app.float.button_border_radius')
})
}
.onClick(async () => {
this.showOverlay = false;
await this.camera.takePicture();
})
.backgroundColor(Color.Black)
.width($r('app.float.button_border_size'))
.height($r('app.float.button_border_size'))
.border({
color: Color.White,
width: $r('app.float.button_border_width'),
radius: $r('app.float.button_border_radius')
})
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
}
.width(CommonConstants.FULL_WIDTH)
.height($r('app.float.camera_lower_height'))
.backgroundColor(Color.Black)
.alignItems(HorizontalAlign.Center)
}
.width(CommonConstants.FULL_WIDTH)
.height(CommonConstants.FULL_Height)
.backgroundColor(Color.Black)
}
.height(CommonConstants.FULL_Height)
}
Real-World Results
This layout provides a clean and professional camera screen with:
- A styled overlay frame (dashed border)
- Centered scan area
- Clear instructions
- Decorative elements
- A circular capture button
Perfect for use cases like:
- IBAN recognition
- Face or ID scanning
- Credit card OCR
- QR/barcode scanning with UI feedback
Limitations and Considerations
- Requires a real HarmonyOS device XComponent and Camera Kit wil not work on emulators.
- Ensure camera permissions are handled Always check runtime permissions before launching the camera view.
Conclusion
Using XComponent + Stack in HarmonyOS is a powerful way to build highly customized, layered camera interfaces that feel modern and intuitive. Whether you’re building a face scanner, IBAN reader, or card recognizer, this layout structure gives you full control over the camera experience.
Top comments (0)