Read the original article:Using overlay for Floating Announcement Layers in HarmonyOS
Context
In HarmonyOS development, it's common to overlay text or custom components on top of existing UI elements. While the Stack component can achieve this, excessive nesting may degrade performance. The overlay property offers a more efficient alternative by reducing one layer of Stack nodes.
Description
The OverlayManager enables developers to display custom UI content above the current page, within the safe area of the window. It sits beneath components like Dialog, Popup, Menu, BindSheet, BindContentCover, and Toast, making it ideal for persistent floating elements.
To use it, retrieve the OverlayManager instance via UIContext.getOverlayManager() and manage floating layers through its methods.
Solution
To implement a fixed announcement banner using overlay, follow these steps:
1. Define a Params class to hold overlay configuration:
class Params {
text: string = '';
offset: Position;
pageInfos: NavPathStack;
overlayContent: ComponentContent<Params>[];
overlayNode: OverlayManager;
constructor(text: string, pageInfos: NavPathStack, overlayContent: ComponentContent<Params>[],
overlayNode: OverlayManager, offset: Position) {
this.text = text;
this.offset = offset;
this.pageInfos = pageInfos;
this.overlayContent = overlayContent;
this.overlayNode = overlayNode;
}
}
2. Create a custom announcement component:
@Builder
function OverlayNode(params: Params) {
Row() {
Text(params.text)
.maxLines(1)
}
.onClick(() => {
let componentContent = params.overlayContent[0];
params.overlayNode.hideComponentContent(componentContent);
params.pageInfos.pushPath({ name: 'NoticePage' });
})
.position({
x: params.offset.x,
y: params.offset.y
})
.hitTestBehavior(HitTestMode.Transparent); // Allows interaction through the overlay
}
3. Initialize the overlay in aboutToAppear():
aboutToAppear(): void {
let componentContent = new ComponentContent(
this.uiContext, wrapBuilder<[Params]>(OverlayNode), this.params
);
this.overlayNode.addComponentContent(componentContent, 0);
this.overlayContent.push(componentContent);
}
4. Clean up the overlay in aboutToDisappear():
aboutToDisappear(): void {
let componentContent = this.overlayContent.pop();
this.overlayNode.removeComponentContent(componentContent);
}
5. Full implementation example:
import { ComponentContent, OverlayManager } from '@kit.ArkUI'
export class Params {
text: string = '';
offset: Position;
pageInfos: NavPathStack;
overlayContent: ComponentContent<Params>[];
overlayNode: OverlayManager;
constructor(text: string, pageInfos: NavPathStack, overlayContent: ComponentContent<Params>[],
overlayNode: OverlayManager, offset: Position) {
this.text = text;
this.offset = offset;
this.pageInfos = pageInfos;
this.overlayContent = overlayContent;
this.overlayNode = overlayNode;
}
}
@Builder
export function OverlayNode(params: Params) {
Row({ space: 5 }) {
Text('Click to view')
.fontSize(12)
.fontColor(Color.Red);
Text(params.text)
.fontWeight(400)
.lineHeight(16)
.fontSize(14)
.maxLines(1)
.layoutWeight(1);
Text('>')
.fontSize(12)
.fontColor(Color.Orange);
}
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.SpaceBetween)
.onClick(() => {
let componentContent = params.overlayContent[0];
params.overlayNode.hideComponentContent(componentContent);
})
.position({
x: params.offset.x,
y: params.offset.y
})
.expandSafeArea([SafeAreaType.KEYBOARD])
.height(35)
.width('100%')
.backgroundColor(Color.Grey)
.hitTestBehavior(HitTestMode.Transparent);
}
@Entry
@Component
struct MainPage {
@State text: string = 'Team meeting next Monday at 3 PM to review project progress and planning.';
@StorageProp('bottomRectHeight') bottomRectHeight: number = 0;
@StorageProp('topRectHeight') topRectHeight: number = 0;
@StorageLink('componentOffset') componentOffset: Position = { x: 0, y: 1 };
@State isClicked: boolean = false;
pageInfos: NavPathStack = new NavPathStack();
private uiContext: UIContext = this.getUIContext();
private overlayNode: OverlayManager = this.uiContext.getOverlayManager();
private overlayContent: ComponentContent<Params>[] = [];
@Provide params: Params = new Params(this.text, this.pageInfos, this.overlayContent, this.overlayNode, this.componentOffset);
aboutToAppear(): void {
let componentContent = new ComponentContent(
this.uiContext, wrapBuilder<[Params]>(OverlayNode), this.params
);
this.overlayNode.addComponentContent(componentContent, 0);
this.overlayContent.push(componentContent);
}
aboutToDisappear(): void {
let componentContent = this.overlayContent.pop();
this.overlayNode.removeComponentContent(componentContent);
}
build() {
Navigation(this.pageInfos) {
Column() {}
}
.hideTitleBar(true)
.hideToolBar(true)
.backgroundColor('#ffc8c6c6')
.padding({
top: this.getUIContext().px2vp(this.topRectHeight)
});
}
}
Key Takeaways
- The
overlayproperty is ideal for creating floating announcements or persistent UI layers. - Compared to
Stack, it reduces component nesting and improves performance. - Use
ComponentContentfor overlays in frequently refreshed pages to avoid performance loss from repeated destruction and recreation. -
OverlayManagerprovides fine-grained control over floating UI elements and integrates seamlessly with HarmonyOS navigation and layout systems.
Top comments (0)