公司动态
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%设置准确的宽高避免布局重排对于离开可视区域的图片使用loadinglazy列表滑动时暂停图片加载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) // 扩大点击区域