HarmonyOS ArkTS 基础组件的实战技巧(四)
1. Text组件的富文本与自定义样式组合
Text组件作为HarmonyOS ArkTS中最基础的文字展示组件,其实隐藏着不少高级玩法。在实际项目中,我们经常需要实现图文混排、局部文字高亮、点击事件绑定等复杂效果。下面我就分享几个实战中总结的技巧。
Span组件的嵌套使用 是Text组件的核心能力之一。通过Span,我们可以实现同一段文字中不同样式的混合展示。比如在电商App的商品详情页,经常需要展示原价和现价:
Text() {
Span('原价:')
.fontColor('#999')
.fontSize(14)
Span('¥299')
.fontColor('#999')
.fontSize(14)
.decoration({type: TextDecorationType.LineThrough})
Span(' 现价:')
.fontColor('#f30')
.fontSize(16)
Span('¥199')
.fontColor('#f30')
.fontSize(18)
.fontWeight(FontWeight.Bold)
}
这段代码会生成带删除线的原价和红色加粗的现价效果。我在实际开发中发现,Span的链式调用最多支持10层嵌套,超过这个限制会导致编译错误。解决方法是将长文本拆分成多个Text组件组合使用。
自定义文本装饰线 是另一个实用技巧。除了系统提供的下划线、删除线,我们还可以通过border属性模拟更多效果:
Text('限时特惠')
.border({
width: {bottom: 2},
color: 0xFFFF0000,
radius: 0,
style: BorderStyle.Dotted
})
.padding({bottom: 5})
这个例子实现了红色虚线底纹效果,比单纯的实线下划线更显眼。在促销活动场景特别实用。
文本点击事件绑定 需要配合Span的onClick方法。我在开发新闻App时,需要实现话题标签的点击跳转:
Text() {
Span('最新科技动态:')
Span('#HarmonyOS4.0#')
.fontColor('#1a73e8')
.onClick(() => {
router.pushUrl({
url: 'pages/topic/detail',
params: {tag: 'HarmonyOS4.0'}
})
})
}
这里有个坑要注意:Span的点击区域默认没有视觉反馈,最好加上背景色变化效果:
@State isPressed: boolean = false
Span('#HarmonyOS4.0#')
.backgroundColor(this.isPressed ? '#f0f5ff' : '#00000000')
.onClick(() => {...})
.onTouch((event: TouchEvent) => {
if(event.type === TouchType.Down) {
this.isPressed = true
} else if(event.type === TouchType.Up) {
this.isPressed = false
}
})
2. Image组件的高效加载与错误处理
图片加载是移动应用的性能瓶颈之一,我在金融类App开发中就遇到过大量图片导致页面卡顿的问题。经过多次优化,总结出以下实战经验。
渐进式加载策略 能显著提升用户体验。ArkTS的Image组件支持placeholder属性,可以在图片完全加载前显示占位图:
Image('https://example.com/large-image.jpg')
.width(300)
.height(200)
.placeholder($r('app.media.loading'))
.transition({duration: 300, curve: Curve.EaseIn})
更高级的做法是结合Blur效果,先加载小图再过渡到大图:
@State isLoaded: boolean = false
Image(this.isLoaded ?
'https://example.com/high-res.jpg' :
'https://example.com/low-res.jpg')
.blur(this.isLoaded ? 0 : 5)
.onComplete(() => {
this.isLoaded = true
})
错误处理机制 必不可少。网络图片加载失败时,我们需要优雅的降级方案:
@State loadError: boolean = false
Image(loadError ?
$r('app.media.error') :
'https://example.com/unstable-image.jpg')
.onError(() => {
this.loadError = true
})
对于重要图片,还可以加入重试逻辑:
@State retryCount: number = 0
Image(this.loadError && this.retryCount < 3 ?
$r('app.media.retry') :
'https://example.com/unstable-image.jpg')
.onError(() => {
if(this.retryCount < 3) {
setTimeout(() => {
this.retryCount++
this.loadError = false
}, 1000)
} else {
this.loadError = true
}
})
内存优化技巧 也很关键。对于长列表中的图片,建议:
- 使用合适的图片格式:WebP通常比PNG小30%
- 设置准确的宽高避免布局重排
- 对于离开可视区域的图片使用loading="lazy"
- 列表滑动时暂停图片加载
List() {
ForEach(this.items, (item) => {
ListItem() {
Image(item.url)
.width(120)
.height(120)
.loadingStrategy(LoadingStrategy.Lazy)
}
})
}
.onScrollStop(() => {
// 恢复图片加载
})
3. TextInput组件在复杂表单中的联动与验证
表单开发是App中最常见的场景之一,TextInput作为核心输入组件,其高级用法值得深入探讨。
动态输入验证 能即时反馈用户输入状态。比如手机号输入框:
@State mobile: string = ''
@State mobileValid: boolean = false
TextInput({placeholder: '请输入手机号'})
.width('100%')
.onChange((value: string) => {
this.mobile = value
this.mobileValid = /^1[3-9]\d{9}$/.test(value)
})
.border({
color: this.mobileValid ? '#67C23A' :
(this.mobile ? '#F56C6C' : '#DCDFE6'),
width: 1
})
表单联动校验 在注册流程中很常见。比如密码和确认密码的一致性检查:
@State password: string = ''
@State confirm: string = ''
@State pwdMatch: boolean = true
Column() {
TextInput({placeholder: '设置密码'})
.type(InputType.Password)
.onChange((value) => {
this.password = value
this.checkMatch()
})
TextInput({placeholder: '确认密码'})
.type(InputType.Password)
.onChange((value) => {
this.confirm = value
this.checkMatch()
})
if(!this.pwdMatch) {
Text('两次输入密码不一致')
.fontColor('#F56C6C')
.fontSize(12)
}
}
checkMatch() {
this.pwdMatch = this.password === this.confirm
}
输入法适配 是容易被忽视的细节。在开发多语言App时,需要特别关注:
TextInput()
.enterKeyType(EnterKeyType.Search) // 键盘回车键样式
.inputMethod(InputMethod.Number) // 限制数字键盘
.onAccept((value) => {
// 处理回车键事件
})
对于搜索框,还可以加入防抖优化:
@State searchText: string = ''
timer: number = 0
TextInput({placeholder: '搜索'})
.onChange((value) => {
this.searchText = value
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.doSearch()
}, 500)
})
4. Button组件的状态管理与交互反馈
按钮作为最重要的交互组件,其状态管理和动效设计直接影响用户体验。
多状态样式管理 可以通过@Styles实现统一维护:
@Styles function primaryButton() {
.width('80%')
.height(40)
.borderRadius(20)
.stateEffect(true)
}
@Styles function disabledButton() {
.opacity(0.6)
.stateEffect(false)
}
Button('提交')
.primaryButton()
.enabled(!this.isLoading)
.disabledButton(!this.isLoading)
.onClick(() => {
this.submitForm()
})
加载状态反馈 能有效防止重复提交:
@State isLoading: boolean = false
Button(this.isLoading ? '' : '提交')
.width(120)
.onClick(() => {
this.isLoading = true
this.submit().finally(() => {
this.isLoading = false
})
})
.enabled(!this.isLoading)
.backgroundColor(this.isLoading ? '#ccc' : '#007DFF')
if(this.isLoading) {
LoadingProgress()
.color(Color.White)
.width(20)
.height(20)
}
高级点击效果 可以提升交互体验。比如水波纹效果:
@State rippleX: number = 0
@State rippleY: number = 0
@State rippleShow: boolean = false
Button('点击我')
.overlay(
this.rippleShow ?
Circle()
.width(200)
.height(200)
.position({x: this.rippleX, y: this.rippleY})
.backgroundColor('#ffffff40')
.transition({duration: 600, curve: Curve.Friction})
: null
)
.onTouch((event) => {
if(event.type === TouchType.Down) {
this.rippleX = event.touches[0].x
this.rippleY = event.touches[0].y
this.rippleShow = true
} else if(event.type === TouchType.Up) {
setTimeout(() => {
this.rippleShow = false
}, 600)
}
})
按钮组合技巧 在工具栏场景很实用。比如通过RowSplit实现可调整大小的按钮组:
RowSplit() {
Button('撤销')
.layoutWeight(1)
Divider()
.vertical(true)
.height('80%')
Button('重做')
.layoutWeight(1)
}
.height(40)
.borderRadius(4)
.border({width:1, color:'#ddd'})
在实际项目中,我还发现Button的点击热区经常需要调整,可以通过padding扩大点击区域:
Button('小按钮')
.width(60)
.height(30)
.padding(10) // 扩大点击区域
更多推荐


所有评论(0)