HarmonyOS 鸿蒙Next如何设计一段文本超过3行时,在三行末尾加上显示'..全文'等自定义文案
HarmonyOS 鸿蒙Next如何设计一段文本超过3行时,在三行末尾加上显示’…全文’等自定义文案
如何设计一段文本超过3行的时候,在三行的末尾加上显示’…全文’等自定义文案
2 回复
import { inspector } from '[@kit](/user/kit).ArkUI';
import componentUtils from '[@ohos](/user/ohos).arkui.componentUtils';
import measure, { MeasureOptions } from '[@ohos](/user/ohos).measure'
interface TextSpan {
type: 'text' | 'image'
content: string | Resource
}
[@Entry](/user/Entry)
[@Component](/user/Component)
export struct TextDemo1 {
private maxLines: number = 3
private fontSize: string = '60px'
private iconWidth: number = 60
private suffixCollapseTxt: string = '收起';
private suffixExpandTxt: string = '... 全文';
private textKey: string = Math.random() + ''
[@State](/user/State) message: string =
`“加快形成新质生产力,增强发展新动能。” 新时代推动东北[图标]全面振兴座谈会上首次提到了新质生产力。此后,新质生产力被[图标]写入中央经济工作会议以及2024年政府[图标]工作报告,成为指导中国经济[图标]发展的重要路标。 本次新时代推动中部地区崛起座谈会,同样提到新质生产力。`
[@State](/user/State) isCollapse: boolean = true
[@State](/user/State) textMaxLine: number = Infinity
[@State](/user/State) needClip: boolean = false
[@State](/user/State) renderSpan: TextSpan[] = []
private textMeasure: TextMeasure = new TextMeasure(this.fontSize)
private clipIndex: number | null = null
private collapseHeight: number = 0
private expandSpan: TextSpan[] = []
private collapseSpan: TextSpan[] = []
aboutToAppear(): void {
const listener = inspector.createComponentObserver(this.textKey)
listener.on('layout', this.onLayoutComplete)
// listener.on('draw', this.onDrawComplete)
}
onLayoutComplete = () => {
if (!this.isCollapse) {
return
}
const size = componentUtils.getRectangleById(this.textKey).size
if (this.clipIndex === null) {
const result = this.textMeasure.measureClipSpan(size.width, this.message, this.maxLines, this.suffixCollapseTxt,
this.iconWidth)
this.clipIndex = result.clipIndex
this.expandSpan = result.textSpan.slice()
if (this.clipIndex > -1) {
this.needClip = true
this.textMaxLine = this.maxLines
const renderSpan = result.textSpan.slice(0, this.clipIndex + 1)
this.renderSpan = [...renderSpan]
return
}
}
if (!this.collapseHeight && this.needClip) {
this.collapseHeight = size.height
this.textMaxLine = Infinity
return
}
if (this.needClip && size.height > this.collapseHeight) {
if (this.renderSpan[this.clipIndex].type === 'image') {
this.renderSpan.splice(this.clipIndex, 1)
} else {
const oldText = this.renderSpan[this.clipIndex].content.toString()
const newText = oldText.substring(0, oldText.length - 1)
this.renderSpan[this.clipIndex] = { type: 'text', content: newText }
this.renderSpan = [...this.renderSpan]
// this.renderSpan.splice(this.clipIndex, 1, { type: 'text', content: newText })
}
} else {
this.collapseSpan = [...this.renderSpan]
}
}
build() {
Text(this.message) {
ForEach(this.renderSpan, (item: TextSpan) => {
if (item.type === 'text') {
Span(item.content)
}
if (item.type === 'image') {
ImageSpan(item.content).width(this.iconWidth + 'px')
}
})
if (this.needClip) {
Span(this.isCollapse ? this.suffixExpandTxt : this.suffixCollapseTxt)
.onClick(() => {
this.isCollapse = !this.isCollapse
this.renderSpan = this.isCollapse ? [...this.collapseSpan] : [...this.expandSpan]
})
.fontColor(Color.Blue)
}
}
.maxLines(this.textMaxLine)
.fontSize(this.fontSize)
.key(this.textKey)
}
}
interface MeasureResult {
textSpan: TextSpan[],
clipIndex: number
}
class TextMeasure {
static keyMap: Record<string, number> = {}
options: MeasureOptions = { textContent: '' }
constructor(fontSize?: string) {
this.options.fontSize = fontSize
}
measureClipSpan(containerWidth: number, text: string, maxLines: number, suffixCollapseTxt: string,
iconWidth?: number): MeasureResult {
let maxWidth = containerWidth * maxLines
let textArr = text.split('')
let sumWidth = 0
let textSpan: TextSpan[] = []
let txtCache: string = ''
let imageSpanFlag = false
let clipIndex: number = -1
let suffixWidth = this.measureSuffixWidth(suffixCollapseTxt)
textArr.forEach((i: string, idx: number) => {
let itemWidth = 0
if (i === '[') {
imageSpanFlag = true
textSpan.push({
type: 'text',
content: txtCache
})
txtCache = ''
return
} else if (i === ']') {
imageSpanFlag = false
itemWidth = iconWidth || 0
textSpan.push({
type: 'image',
content: $r('app.media.app_icon')
})
return
}
if (imageSpanFlag) {
return
}
if (!itemWidth) {
itemWidth = this.measureOneText(i)
}
sumWidth += itemWidth
if ((sumWidth + suffixWidth) >= maxWidth && clipIndex < 0) {
console.log('clipIndex____' + i + " " + idx)
clipIndex = textSpan.length
textSpan.push({
type: 'text',
content: txtCache
})
txtCache = ''
}
txtCache += i
if (idx === textArr.length - 1) {
textSpan.push({
type: 'text',
content: txtCache
})
txtCache = ''
}
})
return {
textSpan: textSpan,
clipIndex: clipIndex
}
}
measureSuffixWidth(text: string) {
return text.split('').reduce((sum, item) => sum + this.measureOneText(item), 0)
}
measureOneText(text: string): number {
let isNumber = /[0-9]/.test(text)
let isChinese = !isNumber && /[\u4e00-\u9fa5]/.test(text)
if (isChinese && TextMeasure.keyMap['__chinese__']) {
return TextMeasure.keyMap['__chinese__']
}
if (isNumber && TextMeasure.keyMap['__number__']) {
return TextMeasure.keyMap['__number__']
}
if (TextMeasure.keyMap[text]) {
return TextMeasure.keyMap[text]
}
this.options.textContent = text
let size = measure.measureText(this.options)
if (isChinese) {
TextMeasure.keyMap['__chinese__'] = size
} else if (isNumber) {
TextMeasure.keyMap['__number__'] = size
} else {
TextMeasure.keyMap[text] = size
}
return size
}
}
更多关于HarmonyOS 鸿蒙Next如何设计一段文本超过3行时,在三行末尾加上显示'..全文'等自定义文案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next系统中,如果你希望在设计UI时实现当文本超过三行时在末尾显示“…全文”等自定义文案,可以通过以下方式实现:
-
文本测量:首先,你需要使用
TextPainter
或Canvas
来测量文本的绘制高度,以确定文本是否超过三行。这通常涉及设置文本的字体样式、行高和最大行数等参数。 -
文本截断与拼接:一旦确定文本超过三行,你需要截断文本,并在末尾拼接上“…全文”等自定义文案。这可以通过字符串操作来实现。
-
自定义Widget:为了方便复用和统一管理,你可以创建一个自定义的Widget,该Widget接收原始文本和自定义文案作为参数,并根据上述逻辑进行文本处理和显示。
-
布局更新:在文本内容或显示状态(如是否展开全文)发生变化时,确保触发UI布局的更新。
实现上述功能时,请注意处理文本的换行逻辑、字符编码以及不同语言环境下的文本显示问题。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html