DEV Community

Cover image for Automatic Focus Acquisition, Global Popups, Data Change Monitoring, Size Display Anomalies
kouwei qing
kouwei qing

Posted on

Automatic Focus Acquisition, Global Popups, Data Change Monitoring, Size Display Anomalies

[Daily HarmonyOS Next Knowledge] Automatic Focus Acquisition, Global Popups, Data Change Monitoring, Size Display Anomalies, One-Click Closure of All Pages

1. HarmonyOS TextInput automatic focus issue?

A TextInput and a button are arranged vertically on the page. Clicking the button triggers a popup using CustomDialogController. After closing the popup, the TextInput automatically gains focus. How to prevent automatic focus acquisition? Setting .defaultFocus(false).groupDefaultFocus(false) is ineffective.

Use requestFocus as shown in the demo:

import prompt from '@ohos.prompt';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  @State isboole: boolean = true;

  build() {
    Row() {
      Column() {
        Text(this.message)
          .id('1sa')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('I will get focus')
          .key('BBB')
        TextInput({ placeholder: 'AAAAA' })
          .id('AAAA1')
          .type(InputType.Normal)
        Button('Click to transfer focus')
          .onClick(() => {
            let res = focusControl.requestFocus('BBB')
            if (res) {
              prompt.showToast({ message: 'Request success' })
            } else {
              prompt.showToast({ message: 'Request failed' })
            }
          })
        Button('Click to get focus')
          .onClick(() => {
            this.isboole = true
            sendEventByKey("AAAA1", 10, "") // Send click event to component with id "longClick"
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

2. How to encapsulate global popups in HarmonyOS? What is the best practice?

  1. What is the optimal way to encapsulate public popups?
  2. How to use popups in non-UI components?

To encapsulate a globally usable popup, use promptAction to wrap a custom dialog:

Reference documentation: See the example code for promptAction.openCustomDialog (https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-promptaction-V5#promptactionopencustomdialog11)

Sample code:

let customDialogId: number = 0

export function showCustomDialog() {
  let result: CustomDialogController = new CustomDialogController({
    builder: SimpleDialog({}),
    alignment: DialogAlignment.Bottom,
    customStyle: true,
    offset: {
      dx: 0,
      dy: CommonConstants.DY_OFFSET
    }
  })
  result.open()
}

...

onClick: () => {
  promptAction.openCustomDialog({
    builder: showCustomDialog.bind(this)
  }).then((dialogId: number) => {
    customDialogId = dialogId;
  })
}

// Confirm/cancel popup
promptAction.closeCustomDialog(customDialogId)
Enter fullscreen mode Exit fullscreen mode

3. How to monitor data changes of isNormalMode within a @builder decorator in HarmonyOS?

Need to update the Image component's source when isNormalMode changes. Currently, if written directly in build without @builder, the Image updates normally. When written in @builder, data changes are not detected. How to monitor data changes within @builder?

@State isNormalMode: boolean = true;

@Builder
showImage(source: string | Resource, onImageClick: () => void = () => {}) {

  Image(source)
    .fitOriginalSize(true)
    .objectFit(ImageFit.Auto)
    .width(Constants.FULL_PARENT)
    .onClick(() => {
      console.log("obclick")
      onImageClick();
    })
    .padding({ /// Inner area padding
      top: $r('app.float.screen_margin_left_right')
    })
}

build() {
  Stack({ alignContent: Alignment.TopStart }) {
    Flex({ direction: FlexDirection.Column }) {

      TitleBar({ model: $model })

      Column() {

        this.showImage(this.isNormalMode ? $r('app.media.normal_mode_selected') : $r('app.media.normal_mode_unselect'), () => {
          console.log("setImageobclick")
          this.isNormalMode = true;
        });

        this.showImage(this.isNormalMode ? $r('app.media.easy_mode_unselect') : $r('app.media.easy_mode_selected'), () => {
          console.log("setImageobclick")
          this.isNormalMode = false;
        });
      }
      .padding({ /// Inner area padding
        left: $r('app.float.screen_margin_left_right'),
        right: $r('app.float.screen_margin_left_right'),
      })
      .width(Constants.FULL_PARENT)
      .height(Constants.FULL_PARENT)
    }
  }
  .width(Constants.FULL_PARENT)
  .height(Constants.FULL_PARENT)
}
Enter fullscreen mode Exit fullscreen mode

When calling a @Builder-decorated function, parameters are passed by value. Changes to state variables (passed by value) do not trigger UI refresh within @builder. For state variables, use reference passing.

Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-builder-V5

Sample code:

class ImageParams {
  imageUrl: string | Resource = ''
  onImageClick: () => void = () => {
  }
}

@Entry
@Component
struct Index {
  @State isNormalMode: boolean = true;
  @State imgUrl: string | Resource = $r('app.media.img1')

  @Builder
  showImageNew(params: ImageParams) {

    Image(params.imageUrl)
      .fitOriginalSize(true)
      .objectFit(ImageFit.Auto)
      .width(200)
      .onClick(() => {
        console.log("obclick")
        params.onImageClick();
      })
  }

  @Builder
  showImage(source: string | Resource, onImageClick: () => void = () => {
  }) {
    Image(source)
      .fitOriginalSize(true)
      .objectFit(ImageFit.Auto)
      .width(200)
      .onClick(() => {
        console.log("obclick")
        onImageClick();
      })
  }

  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      Flex({ direction: FlexDirection.Column }) {

        Column() {

          Text("Status: " + this.isNormalMode)
          Text(" imgUrl: " + JSON.stringify(this.imgUrl))

          this.showImage(this.imgUrl, () => {
            console.log("setImageobclick")
            this.isNormalMode = !this.isNormalMode;
            this.imgUrl = this.isNormalMode ? $r('app.media.img1') : $r('app.media.img2')
          })

          Button("Change type (only status, image unchanged)").onClick(() => {
            this.isNormalMode = !this.isNormalMode
          })

          //=================Use the following approach==========================
          this.showImageNew(
            {
              imageUrl: this.imgUrl,
              onImageClick: () => {
                this.isNormalMode = !this.isNormalMode;
                this.imgUrl = this.isNormalMode ? $r('app.media.img1') : $r('app.media.img2')
              }
            }
          )
          if (this.isNormalMode) {
            this.showImageNew(
              {
                imageUrl: $r("app.media.img1"),
                onImageClick: () => {
                  this.isNormalMode = !this.isNormalMode;
                  this.imgUrl = this.isNormalMode ? $r('app.media.img1') : $r('app.media.img2')
                }
              }
            )
          } else {
            this.showImageNew(
              {
                imageUrl: $r("app.media.img2"),
                onImageClick: () => {
                  this.isNormalMode = !this.isNormalMode;
                  this.imgUrl = this.isNormalMode ? $r('app.media.img1') : $r('app.media.img2')
                }
              }
            )
          }

        }
        .width("100%")
        .height("100%")
      }
    }
    .width("100%")
    .height("100%")
  }
}
Enter fullscreen mode Exit fullscreen mode

4. HarmonyOS component size display anomaly?

In the following code, the width is set to 100%, but the actual width does not fill the screen as shown in the screenshot.

@Entry
@Component
struct AlignRuleTestPage {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('AlignRuleTestPageHelloWorld')
        .fontSize(30)
        .width("100%")
        .aspectRatio(6)
        .margin({
          top: "14%"
        })
        .backgroundColor(Color.Red)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          top: { anchor: '__container__', align: VerticalAlign.Top },
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          right: { anchor: '__container__', align: HorizontalAlign.End },
        })
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Refer to the demo:

@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('AlignRuleTestPageHelloWorld')
        .fontSize(30)//.aspectRatio(0)  Remove this line
        .margin({
          top: "14%"
        })
        .backgroundColor(Color.Red)
        .fontWeight(FontWeight.Bold)
        .width("100%")
        .height('100%')
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Reference for aspectRatio: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-universal-attributes-layout-constraints-V5

5. How to close all previous pages with one click in HarmonyOS?

After navigating through multiple pages (e.g., A-B-C-D), how to close A, B, and C with one click?

To close pages A and B, use router.clear() to clear all historical pages from the stack, leaving only the current page as the top.

Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-router-V5

Top comments (0)