Read the original article:Implement placeholder text that scrolls up with the content
Problem Description
Set a placeholder text. How to make the placeholder text undeletable and automatically scroll upward as input data increases, as shown below:
Background Knowledge
- Stack is a stacking container where child components are stacked in order, with the latter child components overlaying the previous ones.
- TextArea is a multi-line text input component that automatically wraps text when content exceeds the component width.
- onContentScroll callback is triggered when the TextArea content scrolls, which can be used to make the placeholder text scroll upward with the content.
- onWillDelete callback is triggered when the TextArea is about to delete content, which can be used to make the placeholder text undeletable.
Solution
1.Use the Stack component to combine placeholder text and the text input box.
Stack({ alignContent: Alignment.TopStart }) {
Column() {
Text(this.txt)
// ...
}
TextArea({ text: this.text, controller: this.controller })
// ...
}
2.In the onContentScroll callback, modify the offsetY property to change the relative vertical offset of the placeholder text, achieving automatic upward scrolling.
.onContentScroll((totalOffsetX: number, totalOffsetY: number) => {
if ((this.getUIContext().px2vp(totalOffsetY) - 8) > 0) {
this.offsetY = 0
} else if ((this.getUIContext().px2vp(totalOffsetY) - 8) < -16) {
this.offsetY = -16
} else {
this.offsetY = (this.getUIContext().px2vp(totalOffsetY) - 8)
}
})
3.In the onWillDelete callback, determine whether the cursor can move backward based on deleteOffset, making the placeholder text undeletable.
.onWillDelete((info: DeleteValue) => {
if (info.deleteOffset === 1 || this.text === ' ') {
return false
}
return true;
})
Complete example is as follows:
@Entry
@Component
struct Index {
@State txt: string = 'Placeholder Text'
@State text: string = ' '
@State textValue: string = ''
@State txtWidth: number = 0
@State offsetY: number = 0
controller: TextAreaController = new TextAreaController()
build() {
Row() {
Column() {
Stack({ alignContent: Alignment.TopStart }) {
Column() {
Text(this.txt)
.margin({ left: 14 })
.fontSize(15)
.fontColor(Color.Blue)
.onAreaChange((oldValue: Area, newValue: Area) => {
this.txtWidth = newValue.width as number
})
.offset({ x: 0, y: this.offsetY })
}
.width(200)
.height(92)
.background(Color.White)
.alignItems(HorizontalAlign.Start)
.border({ width: 1 })
.margin({ top: 8 })
.clip(true)
TextArea({ text: this.text, controller: this.controller })
.width(200)
.height(100)
.wordBreak(WordBreak.BREAK_ALL)
.textIndent(this.txtWidth)
.borderRadius(0)
.fontColor(Color.Black)
.onChange((info) => {
this.text = info
this.textValue = this.text.trim()
})
.backgroundColor(Color.Transparent)
.onContentScroll((totalOffsetX: number, totalOffsetY: number) => {
if ((this.getUIContext().px2vp(totalOffsetY) - 8) > 0) {
this.offsetY = 0
} else if ((this.getUIContext().px2vp(totalOffsetY) - 8) < -16) {
this.offsetY = -16
} else {
this.offsetY = (this.getUIContext().px2vp(totalOffsetY) - 8)
}
})
.onWillDelete((info: DeleteValue) => {
if (info.deleteOffset === 1 || this.text === ' ') {
return false
}
return true;
})
.onDidDelete((info: DeleteValue) => {
if (info.deleteOffset === 0) {
this.text = ' '
}
})
.onTextSelectionChange((selectionStart: number, selectionEnd: number) => {
if (selectionStart === 0) {
selectionStart === selectionEnd ? this.controller.caretPosition(1) :
this.controller.setTextSelection(1, selectionEnd)
}
})
}
}
.width('100%')
}
.height('100%')
}
}

Top comments (0)