DEV Community

Cover image for Component Position Determination, List Item Screen Exit Lifecycle, Calendar View Component, Component Update Issues
kouwei qing
kouwei qing

Posted on

Component Position Determination, List Item Screen Exit Lifecycle, Calendar View Component, Component Update Issues

[Daily HarmonyOS Next Knowledge] Component Position Determination, List Item Screen Exit Lifecycle, Calendar View Component, Component Update Issues, Vertical Floating Issues

1. How to determine the position of each component or card on the screen in HarmonyOS?

Use componentUtils.getRectangleById to obtain the component instance object by ID. The object returns the component's coordinates and size synchronously to the developer.

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

getRectangleById(id: string): ComponentInfo
// Obtain the component instance object by ID. The coordinates and size are returned synchronously to the developer via the instance object.
Enter fullscreen mode Exit fullscreen mode

2. In HarmonyOS, when a ListItem in LazyForEach scrolls off-screen, onDisAppear is not called. Is this normal?

build() {
  Row() {
    List({ space: commonConst.LIST_ITEM_SPACE }) {
      LazyForEach(this.goodsListData, (item: GoodsListItemType) => {
        ListItem() {
          Row() {
            Column() {
              Image(item?.goodsImg)
                .width(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
                .height(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
            }
            .width(commonConst.GOODS_IMAGE_WIDTH)
            .height(commonConst.LAYOUT_WIDTH_OR_HEIGHT)

            Column() {
              Text(item?.goodsName)
                .fontSize(commonConst.NORMAL_FONT_SIZE)
                .margin({ bottom: commonConst.BIGGER_FONT_SIZE })
              Text(item?.advertisingLanguage)
                .fontColor($r('app.color.gray'))
                .fontSize(commonConst.GOODS_EVALUATE_FONT_SIZE)
                .margin({ right: commonConst.MARGIN_RIGHT, bottom: commonConst.BIGGER_FONT_SIZE })
              Row() {
                Text(item?.evaluate)
                  .fontSize(commonConst.GOODS_EVALUATE_FONT_SIZE)
                  .fontColor($r('app.color.deepGray'))
                Text(item?.price).fontSize(commonConst.NORMAL_FONT_SIZE).fontColor($r('app.color.freshRed'))
              }
              .justifyContent(FlexAlign.SpaceAround)
              .width(commonConst.GOODS_LIST_WIDTH)
            }
            .padding(commonConst.GOODS_LIST_PADDING)
            .width(commonConst.GOODS_FONT_WIDTH)
            .height(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
          }
          .justifyContent(FlexAlign.SpaceBetween)
          .height(commonConst.GOODS_LIST_HEIGHT)
          .width(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
        }.onAppear(() => {
          console.log("lzqnet  dddd")
        }).onDisAppear(() => {
          console.log("lzqnet   eeeee")
        })
Enter fullscreen mode Exit fullscreen mode

The onDisAppear callback is not triggered when the item scrolls off-screen. Why?

onAppear(() => {
  console.log("lzqnet  dddd")
}).onDisAppear(() => {
  console.log("lzqnet   eeeee")
})
Enter fullscreen mode Exit fullscreen mode

Explanation: onDisAppear triggers when the component is unloaded from the component tree. To monitor visibility changes, use onVisibleAreaChange.

3. Reference for HarmonyOS calendar component in month view mode?

Supports switching to previous month (1st day), next month, or today. Allows date selection when browsing a specific month.

// import { Lunar } from 'lunar-javascript'
import dateClass from '../beans/DateClass'
import dayjs from 'dayjs'

@Entry
@Component
struct DatePickerDialogExample01 {
  weekList: Array<string> = ['', '', '', '', '', '', '']
  @State show: boolean = false
  @State selectDate: dayjs.Dayjs = dayjs()
  @State selectMonth: string = dayjs().format('YYYY年MM月')
  @State dataList: Array<dateClass> = []
  @State monthParam: number = 0 // Parameter for calendar navigation

  build() {
    Column() {
      Button('弹出或关闭日历')
        .height(36)
        .fontSize(20)
        .fontColor(Color.White)
        .onClick(() => {
          this.show = !this.show
          this.selectDate = dayjs()
          this.monthParam = 0
          this.getDataList(this.monthParam)
        })

      if (this.show) {
        Row() {
          Text(this.selectMonth)
            .fontSize(16)
            .fontColor(Color.White)
          Row() {
            Row() {
              Image($r('app.media.startIcon'))
                .width(24)
                .height(12)
                .backgroundColor(Color.Black)
            }
            .height('100%')
            .width(48)
            .alignItems(VerticalAlign.Center)
            .justifyContent(FlexAlign.Center)
            .onClick(() => {
              this.monthParam--
              this.getDataList(this.monthParam)
            })

            Row() {
              Image($r('app.media.startIcon'))
                .width(24)
                .height(12)
                .backgroundColor(Color.Black)
            }
            .height('100%')
            .width(48)
            .alignItems(VerticalAlign.Center)
            .justifyContent(FlexAlign.Center)
            .onClick(() => {
              this.monthParam++
              this.getDataList(this.monthParam)
            })
          }
        }
        .height(48)
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)

        Row() {
          ForEach(this.weekList, (item: string) => {
            Text(item)
              .fontSize(16)
            Row() {
              ForEach(this.weekList, (item: string) => {
                Text(item)
                  .fontSize(16)
                  .fontColor(Color.White)
                  .width(48)
                  .textAlign(TextAlign.Center)
              })
            }
            .width('100%')

            Grid() {
              ForEach(this.dataList, (item: dateClass) => {
                GridItem() {
                  Column() {
                    Text(item.day.toString())
                      .fontSize(16)
                      .fontColor(Color.White)
                      .width(48)
                      .textAlign(TextAlign.Center)
                      .opacity(item.isToMonth ? 1 : 0.4)
                    Text(item.lunarDay)
                      .fontSize(12)
                      .fontColor(Color.White)
                      .width(48)
                      .textAlign(TextAlign.Center)
                      .opacity(item.isToMonth ? 1 : 0.4)
                  }
                  .width(48)
                  .height(48)
                  .alignItems(HorizontalAlign.Center)
                  .justifyContent(FlexAlign.Center)
                  .backgroundColor(item.dayjsObj.format('YYYY-MM-DD') ==
                  dayjs().format('YYYY-MM-DD') ?
                    '#007DFF' : Color.Black)
                  .borderWidth(1)
                  .borderColor(item.dayjsObj.format('YYYY-MM-DD') ==
                  this.selectDate.format('YYYY-MM-DD') ?
                    '#007DFF' : Color.Black)
                  .onClick(() => {
                    this.selectDate = item.dayjsObj
                  })
                }
              })
            }
            .height(288)
            .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
            .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
            .columnsGap(0)
            .rowsGap(0)
          }
        }
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Black)
        .justifyContent(FlexAlign.Center)
        .padding({ left: 12, right: 12 })
      }
      getDataList(param: number) {
        this.dataList = []
        let firstDate = dayjs().add(param, 'month').startOf('month') // First day of the month
        let afterDate = dayjs().add(param, 'month').endOf('month') // Last day of the month
        this.selectMonth = firstDate.format('YYYY年MM月')
        let frontDay = 0 // Days to offset for calendar display
        if (firstDate.day() == 0) {
          frontDay = 6
        } else {
          frontDay = firstDate.day() - 1
        }
        let showFirstDay = firstDate.subtract(frontDay, 'day') // First visible day in calendar
        for (let i = 0;i < 42; i++) {
          let dayjsObj = showFirstDay.add(i, 'day')
          let day = dayjsObj.date()
          let lunarDay = ''
          // if (Lunar.fromDate(dayjsObj.toDate()).getFestivals().length !== 0) { // Show festival
          // lunarDay = Lunar.fromDate(dayjsObj.toDate()).getFestivals()[0]
          // } else if (Lunar.fromDate(dayjsObj.toDate()).getJieQi() !== '') { // Show solar term
          // lunarDay = Lunar.fromDate(dayjsObj.toDate()).getJieQi()
          // } else { // Show lunar date
          // lunarDay = Lunar.fromDate(dayjsObj.toDate()).getDayInChinese()
          // }
          let isToMonth = true
          if (dayjsObj.isBefore(firstDate) || dayjsObj.isAfter(afterDate)) {
            isToMonth = false
          }
          this.dataList.push(new dateClass(dayjsObj, day, lunarDay, isToMonth))
        }
        // console.log('aboutToAppear',JSON.stringify(this.dataList))
      }

      aboutToAppear() {
        this.getDataList(this.monthParam)
      }
    }
    // @ts-ignore
    import dayjs from 'dayjs'

    export default class DateClass {
      dayjsObj: dayjs.Dayjs // dayjs time object
      day: number // Day of the month
      lunarDay: string // Lunar calendar date
      isToMonth: boolean // Whether it belongs to the current month

      constructor(dayjsObj: dayjs.Dayjs, day: number, lunarDay: string, isToMonth: boolean) {
        this.dayjsObj = dayjsObj
        this.day = day
        this.lunarDay = lunarDay
        this.isToMonth = isToMonth
      }
    }
Enter fullscreen mode Exit fullscreen mode

4. Components created via WrappedBuilder.builder in HarmonyOS cannot be updated. Does WrappedBuilder.builder not support updates?

When creating a component with WrappedBuilder.builder, the initial build works, but state changes in the parent component do not trigger rebuilds.

build() {
  Column() {
    if (this.dxItemRenderService && this.section && this.templateItem) {

      Text(this.section?.template?.name)
      this.dxItemRenderService?.getDxWrappedBuilder().builder(this.section)
      Text(this.section?.template?.name)
    } else {
      Text('not found' + this.templateItem?.name);
    }
  }
  .width('100%')
  .opacity(this.searchBarHasRender())
  .onAreaChange(this.searchBarAreaChange.bind(this))
}
Enter fullscreen mode Exit fullscreen mode

Explanation: BuilderNode does not automatically update state variables. Manually call the update method to refresh content.

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

5. In HarmonyOS, setting responseRegion to float vertically by 50% only works for the upper half?

Code example:

Column() {
  Blank().height(200)
  Text("按钮1").onClick(() => {})
    .height(60)
    .stateStyles({
      pressed: {
        .backgroundColor(Color.Red)
      },
      normal: {
        .backgroundColor(Color.Blue)
      }
    })
    .responseRegion({ x: 0, y: '-50%', width: '100%', height: '200%' })
  Blank().height(30)
}
Enter fullscreen mode Exit fullscreen mode

Issue: The lower half of the response region does not work due to layout遮挡 (similar to z-index conflicts in Stack).

Solutions:

  1. Replace Blank with Row.
  2. Set zIndex(1) on the Text component to bring it to the top layer.

Fixed example:

Column() {
  Blank().height(200)
  Text("按钮1").onClick(() => {})
    .height(60)
    .stateStyles({
      pressed: {
        .backgroundColor(Color.Red)
      },
      normal: {
        .backgroundColor(Color.Blue)
      }
    })
    .responseRegion({ x: 0, y: '-50%', width: '100%', height: '200%' })
    .zIndex(1)  // Ensure the component is on top
  Blank().height(30)
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)