[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?
-
backgroundImageResizable
has no effect, whileImage
withresizable
works. - 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%')
}
}