DEV Community

Cover image for Top Status Bar, Text Max Lines Suffix, Popup Background, Status Bar Color, Navigation
kouwei qing
kouwei qing

Posted on

Top Status Bar, Text Max Lines Suffix, Popup Background, Status Bar Color, Navigation

[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));
Enter fullscreen mode Exit fullscreen mode

Reference documents:

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.

  1. 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.
  2. 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.
  3. 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()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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")
  })
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)