Zum Inhalt springen

State Variables, Animation UI Residue, Tab Control Display, ob Prefix Issue, Text Background Stretching

[Daily HarmonyOS Next Knowledge] State Variables, Animation UI Residue, Tab Control Display, ob Prefix Issue, Text Background Stretching

1. How to observe changes in multiple variables using one variable in HarmonyOS?

The color of a submit button needs to turn red when multiple values are non-empty, and gray otherwise. Can one variable be used to uniformly observe these values and determine the button’s color, such as Button().backgroundColor(this.color), where this.color depends on multiple input fields?

To bind variables between child and parent components for synchronous updates when one changes:

  • The child component’s variable must be decorated with @Link and not initialized.
  • The parent component’s variable must be decorated with @State.
  • Use the $ symbol when passing values.
import Prompt from '@system.prompt'
@Entry
@Component
struct Index {
  @State inputUserName: string = '张三'
  @State inputUserPsw: string = '张三'
  build() {
    Row() {
      Column() {
        Text(this.inputUserName).fontSize(20)
        LoginInput({ hint: '请输入账号', inputVale: $inputUserName })
        LoginInput({ hint: '请输入账号', inputVale: $inputUserPsw })
      }
      .width('100%')
    }
    .height('100%')
  }
}

@Component
struct LoginInput {
  private hint: string = '请输入账号密码';
  @Link inputVale: string;
  build() {
    TextInput({ placeholder: this.hint, text: this.inputVale })
      .onChange((value) => {
        this.inputVale = value;
        Prompt.showToast({ message: value })
      })
  }
}

2. UI residue during animation in HarmonyOS?

No issues with equal-length transitions, but residue appears when transitioning from 3→2. It disappears after clicking anywhere on the screen.

Refer to the following example to implement联动 (linkage) between custom tabBar and TabContent during switching via onChange:

// xxx.ets
@Entry
@Component
struct TabsExample {
  @State fontColor: string = '#182431'
  @State selectedFontColor: string = '#007DFF'
  @State currentIndex: number = 0
  private controller: TabsController = new TabsController()

  @Builder tabBuilder(index: number, name: string) {
    Column() {
      Text(name)
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(16)
        .fontWeight(this.currentIndex === index ? 500 : 400)
        .lineHeight(22)
        .margin({ top: 17, bottom: 7 })
      Divider()
        .strokeWidth(2)
        .color('#007DFF')
        .opacity(this.currentIndex === index ? 1 : 0)
    }.width('100%')
  }

  build() {
    Column() {
      Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
        TabContent() {
          Column().width('100%').height('100%').backgroundColor('#00CB87')
        }.tabBar(this.tabBuilder(0, 'green'))

        TabContent() {
          Column().width('100%').height('100%').backgroundColor('#007DFF')
        }.tabBar(this.tabBuilder(1, 'blue'))

        TabContent() {
          Column().width('100%').height('100%').backgroundColor('#FFBF00')
        }.tabBar(this.tabBuilder(2, 'yellow'))

        TabContent() {
          Column().width('100%').height('100%').backgroundColor('#E67C92')
        }.tabBar(this.tabBuilder(3, 'pink'))
      }
      .vertical(false)
      .barMode(BarMode.Fixed)
      .barWidth(360)
      .barHeight(56)
      .animationDuration(400)
      .onChange((index: number) => {
        this.currentIndex = index
      })
      .width(360)
      .height(296)
      .margin({ top: 52 })
      .backgroundColor('#F1F3F5')
    }.width('100%')
  }
}

3. Incomplete bottom display of Tabs control in HarmonyOS?

The bottom of the page in TabContent() is not fully displayed.

Set the height of Scroll(this.scroller) to a percentage, ensuring the sum with the upper part is 100%. Reference demo:

@Entry
@Component
struct newPage {
  @State fontColor: string = '#182431'
  @State selectedFontColor: string = '#007DFF'
  @State currentIndex: number = 0
  private controller: TabsController = new TabsController()
  scroller: Scroller = new Scroller()
  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

  @Builder
  tabBuilder(index: number, name: string) {
    Column() {
      Text(name)
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(16)
        .fontWeight(this.currentIndex === index ? 500 : 400)
        .lineHeight(22)
        .margin({ top: 17, bottom: 7 })
      Divider()
        .strokeWidth(2)
        .color('#007DFF')
        .opacity(this.currentIndex === index ? 1 : 0)
    }.width('100%')
  }

  build() {
    Column() {
      Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) {
        TabContent() {
          Column() {
            Row() {
              Text('首页').align(Alignment.Center)
            }
            .justifyContent(FlexAlign.Center)
            .height('10%') 
            .width('100%')
            .padding({ left: 10, right: 10 })
            .backgroundColor(Color.Green)

            Scroll(this.scroller) {
              Column() {
                ForEach(this.arr, (item: number) => {
                  Text(item.toString())
                    .width('90%')
                    .height(150)
                    .backgroundColor(0xFFFFFF)
                    .borderRadius(15)
                    .fontSize(16)
                    .textAlign(TextAlign.Center)
                    .margin({ top: 10 })
                }, (item: string) => item)
              }.width('100%')
            }.height('90%') 
            .scrollable(ScrollDirection.Vertical)
            .friction(0.6)
            .edgeEffect(EdgeEffect.None)
          }
        }
        .tabBar(this.tabBuilder(0, 'green'))

        TabContent() {
          Column().width('100%').height('100%').backgroundColor('#007DFF')
        }.tabBar(this.tabBuilder(1, 'blue'))

      }
      .vertical(false)
      .barMode(BarMode.Fixed)
      .barWidth(360)
      .barHeight(56)
      .animationDuration(400)
      .onChange((index: number) => {
        this.currentIndex = index
      })
      .width('100%')
      .height('100%')
      .backgroundColor('#F1F3F5')
    }.width('100%').height('100%')
  }
}

4. Classes and fields marked with @ObservedV2 and @trace in HarmonyOS have field names prefixed with __ob_ after JSON.stringify?

Field names change after serialization with JSON.stringify, causing deserialization failures.

To handle the __ob_ prefix issue, replace it before serialization. Demo:

import { plainToClass } from "class-transformer";
@ObservedV2
class Son {
  @Trace age: number = 100;
}
class Father {
  son: Son = new Son();
}
@Entry
@Component
struct Index {
  father: Father = new Father();
  aboutToAppear(): void {
    let a = JSON.stringify(this.father);
    let b: Father = plainToClass(Father, this.father);
    console.log(JSON.stringify(convertKeysToCamelCase(this.father)))
  }

  build() {
    Column() {
      Text(`${this.father.son.age}`)
        .onClick(() => {
          this.father.son.age++;
        })
    }
  }
}

// utils.ets
export function underscoreToCamelCase(underscoreString: string): string {
  return underscoreString.replace(/(__ob_)/g, (match: string, letter: string): string => {
    return '';
  });
}
export function convertKeysToCamelCase(obj: ESObject): ESObject {
  if (obj && typeof obj === 'object') {
    const newObj: ESObject = {};
    Object.keys(obj).forEach((key) => {
      if (obj.hasOwnProperty(key)) {
        const newKey = underscoreToCamelCase(key);
        newObj[newKey] = convertKeysToCamelCase(obj[key]);
      }
    })
    return newObj;
  } else {
    return obj;
  }
}

5. Partial stretching of text background in HarmonyOS?

  1. backgroundImageResizable has no effect, while Image with resizable works.
  2. How to make sibling controls adapt to text width.

Refer to the documentation for obtaining text length, and set backgroundImageSize to the text width: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-measure-V5#ZH-CN_TOPIC_0000001884917586__measuremeasuretext

Implementation demo:

import measure from '@ohos.measure'

@Entry
@Component
struct IR240513200608052 {
  @State message: string = 'Hello World啊哈哈哈哈哈哈哈哈哈';
  @State textWidth: number = measure.measureText({ textContent: this.message })

  build() {
    Column() {
      Text(this.message)
        .backgroundImage($r('app.media.startIcon'))
        .backgroundImageResizable({
          slice: {
            top: 3,
            left: 3,
            bottom: 3,
            right: 3
          }
        })
        .backgroundImageSize({ width: this.textWidth })
    }.height('100%').width('100%')
  }
}

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert