[Daily HarmonyOS Next Knowledge] Top Status Bar, Text Max Lines Suffix, Popup Background, Status Bar Color, Navigation
1. HarmonyOS app completely covers the top system status bar information?
// Set the visibility mode of the navigation bar and status bar when the window is in full-screen mode (both need to be displayed, set the parameter to ['status', 'navigation']; if not set, they are hidden by default)
let names: Array<'status' | 'navigation'> = [];
windowClass.setWindowSystemBarEnable(names, (err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in setting the system bar to be visible.');
});
console.info('Succeeded in obtaining the top window. Data: ' + JSON.stringify(data));
Reference documents:
- https://developer.huawei.com/consumer/cn/doc/app/50106
- After setting full-screen immersive mode, use the getWindowAvoidArea() interface to obtain the layout occlusion area, and set it manually in the component based on the avoidance area return value: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-develop-apply-immersive-effects-V5
- If you need to display the status bar and navigation bar, each page must avoid them. To hide them, use setSpecificSystemBarEnabled: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-window-V5
- Enumeration of window content avoidance area types: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-window-V5
2. How to handle the problem of adding "..." and full text after the maximum number of lines for text display in HarmonyOS?
Using calculation to find the width of each character and accumulate it, but sometimes the calculation is inaccurate, especially when the text contains emojis or system line breaks. The wordBreak(WordBreak.BREAK_ALL) API prevents line breaks, but the calculation remains inaccurate with delayed display.
- First, use measure.measureText() to measure the actual text width. Then, use display.getAllDisplays() to get the phone screen width. Compare "screen width * max lines * component width ratio" with "actual text width" to determine if "…expand full text" is needed.
- When "…expand full text" is needed, display only the text content within the fixed height set by the maxLines attribute. When clicking "…expand full text", change it to "…collapse" with a curves.springMotion curve animation, and modify maxLines to -1 in the animateTo closure to make it invalid.
- When "…collapse" is needed, change collapseText to "…expand full text" and set the collapse animation.
// MyText.ets
import measure from '@ohos.measure'
import curves from '@ohos.curves';
import { BusinessError } from '@ohos.base';
import display from '@ohos.display';
@Component
@Preview
export struct MyText {
// Long text
@State longMessage: string = "ArkTS provides concise and natural declarative syntax, componentization mechanisms, data-UI automatic association, and other capabilities, achieving a programming style closer to natural language with higher writing efficiency, bringing developers a high-quality experience of easy learning, understanding, and concise development."
// Maximum display lines
@State lines: number = 3;
// Long text status (expanded or collapsed)
@State collapseText: string = '...Expand Full Text'
// Screen width (px)
screenWidth: number = 0;
// Whether to display "expand" (no need when text is short)
@State isExpanded: boolean = false
// Measured text width (px)
@State textWidth: number = measure.measureText({
textContent: this.longMessage,
fontSize: 15
})
// Get all current display objects
promise: Promise<Array<display.Display>> = display.getAllDisplays()
aboutToAppear() {
console.log(`Text width: ${this.textWidth}`)
this.promise.then((data: Array<display.Display>) => {
console.log(`All screen information: ${JSON.stringify(data)}`)
// Unit: pixels
this.screenWidth = data[0]["width"]
// Compare screen width * max lines * component width ratio with measured text width
this.isExpanded = this.screenWidth * this.lines * 0.8 <= this.textWidth
}).catch((err: BusinessError) => {
console.error(`Failed to obtain all display objects. Code: ${JSON.stringify(err)}`)
})
}
build() {
Row() {
Column() {
if (this.isExpanded) {
Stack({ alignContent: Alignment.BottomEnd }) {
Text(this.longMessage)
.fontSize(15)
.fontColor(Color.Black)
.maxLines(this.lines)
.width('80%')
Row() {
Text(this.collapseText)
.fontSize(15)
.backgroundColor(Color.White)
}
.justifyContent(FlexAlign.End)
.onClick(() => {
if (this.collapseText == '...Expand Full Text') {
this.collapseText = '...Collapse';
// Expand animation
animateTo({
duration: 150,
curve: curves.springMotion(0.5, 0.8),
}, () => {
this.lines = -1; // Invalidate the max lines setting
})
} else {
this.collapseText = '...Expand Full Text';
// Collapse animation
animateTo({
duration: 100,
curve: Curve.Friction,
}, () => {
this.lines = 3; // Only show 3 lines
})
}
})
}
}
else {
Text(this.longMessage)
.fontSize(15)
.fontColor(Color.Black)
}
}
.width('100%')
}
.height('100%')
}
}
// Index.ets
import { MyText } from './MyText'
@Entry
@Component
struct Index {
build(){
Column(){
MyText()
}
}
}
3. How to set the background of a popup bound to a HarmonyOS component to transparent?
Setting popupColor to transparent still results in a default white background.
Image($r("app.media.gray_tips_icon"))
.width($r("app.string.dp15"))
.height($r("app.string.dp15"))
.onClick(() => {
this.tipPopup = !this.tipPopup
})
.bindPopup(this.tipPopup, {
builder: this.popupBuilder(),
placement: Placement.Top,
mask: false,
popupColor: Color.Transparent,
enableArrow: true,
showInSubWindow: false,
onStateChange: (e) => {
if (!e.isVisible) {
this.tipPopup = false
}
},
arrowOffset: $r("app.string.dp50"),
offset: { x: $r("app.string.dp20") },
radius: $r("app.string.dp8")
})
Reference code:
@Entry
@Component
struct PopupExample {
@State handlePopup: boolean = false
@Builder popupBuilder(){
Text('Popup content')
}
build() {
Column() {
Button('PopupOptions')
.onClick(() => {
this.handlePopup = !this.handlePopup
})
.bindPopup(this.handlePopup, {
builder: this.popupBuilder(), // Content
placement: Placement.Bottom, // Popup position
maskColor: Color.Transparent,
popupColor: Color.Transparent, // Popup background color
backgroundBlurStyle: BlurStyle.NONE,
shadow: {
radius: 0
},
onStateChange: (e) => {
console.info(JSON.stringify(e.isVisible))
if (!e.isVisible) {
this.handlePopup = false
}
}
})
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
.padding({ top: 5 })
.backgroundColor(Color.Pink)
}
}
4. Issue with hiding the navBar in HarmonyOS Navigation component?
Setting .hideNavBar(true) in the Navigation component hides the navBar on the home page and secondary pages when using pushPathByName. The home page navBar hides as expected, but the secondary page navBar remains visible with a back button. How to hide it?
Set NavDestination().hideTitleBar(true).
5. How to dynamically set the immersive status bar for each page in HarmonyOS when each page has a different status bar background and font color?
Attempts to call the relevant methods in the onPageShow and aboutToAppear life cycles of each page have failed, as some pages do not update the color correctly, indicating that the system status bar color cannot be changed properly in the life cycle.
Reference code:
// Modified code
Reference:
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { WindowManager } from '../WindowManager';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
hilog.info(0x0000, 'testTag', JSON.stringify(this.context), 'context-tag');
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('enablement/enablement', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
let windowClass = windowStage.getMainWindowSync();
AppStorage.setOrCreate('windowClass', windowClass);
WindowManager.enableFullScreen()
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
// WindowManager.ets
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { LogUtil } from '@pura/harmony-utils';
// Immersive mode utility class
export class WindowManager {
private static readonly TAG: string = "WindowManager---"
// Enable full-screen immersive mode
static enableFullScreen() {
let win: window.Window = AppStorage.get('windowClass')!
win.setWindowLayoutFullScreen(true) // Enable immersive mode with setWindowLayoutFullScreen(true)
const topArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM) // Get safe area height with getWindowAvoidArea
let topHeight = px2vp(topArea.topRect.height)
AppStorage.setOrCreate('topHeight', topHeight) // Convert to vp and store in AppStorage
const bottomArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
let bottomHeight = px2vp(bottomArea.bottomRect.height)
AppStorage.setOrCreate('bottomHeight', bottomHeight) // Convert to vp and store in AppStorage
LogUtil.error(WindowManager.TAG, `topHeight:${topHeight},,,bottomHeight:${bottomHeight}`)
}
// Disable full-screen immersive mode
static disableFullScreen() {
let win: window.Window = AppStorage.get('windowClass')!
win.setWindowLayoutFullScreen(false) // Disable immersive mode with setWindowLayoutFullScreen(false)
AppStorage.setOrCreate('topHeight', 0) // Reset height to zero
}
static settingStatusBarLight() {
let win: window.Window = AppStorage.get('windowClass')!
win.setWindowSystemBarProperties({ statusBarContentColor: '#FFFFFF' }) // Set safe area font color to white
}
static settingStatusBarDark() {
let win: window.Window = AppStorage.get('windowClass')!
win.setWindowSystemBarProperties({ statusBarContentColor: '#000000' }) // Set safe area font color to black
}
static async keepScreenOn(isKeepScreenOn: boolean) {
let win: window.Window = AppStorage.get('windowClass')!
let promise = win.setWindowKeepScreenOn(isKeepScreenOn)
promise?.then(() => {
LogUtil.error(WindowManager.TAG, `${isKeepScreenOn ? "Enabled" : "Disabled"} screen always on successfully`)
}).catch((error: BusinessError) => {
LogUtil.error(WindowManager.TAG,
`${isKeepScreenOn ? "Enable" : "Disable"} screen always on exception, error:${JSON.stringify(error)}`)
});
}
private static async setWindowBrightness(brightness: number) {
let win: window.Window = AppStorage.get('windowClass')!
let promise = win.setWindowBrightness(brightness)
promise?.then(() => {
LogUtil.error(WindowManager.TAG, "Screen brightness set successfully")
}).catch((error: BusinessError) => {
LogUtil.error(WindowManager.TAG, "Screen brightness setting exception, error:" + JSON.stringify(error))
});
}
static setWindowMaxBrightness() {
WindowManager.setWindowBrightness(1)
}
static setWindowDefaultBrightness() {
WindowManager.setWindowBrightness(-1)
}
}
Top comments (0)