HarmonyOS应用开发实战:猫猫大作战-样式抽成可复用类,一处定义、多处复用,还能针对不同状态(正常/按下/禁用)应用不同样式

发布时间:2026/7/28 1:26:08

HarmonyOS应用开发实战:猫猫大作战-样式抽成可复用类,一处定义、多处复用,还能针对不同状态(正常/按下/禁用)应用不同样式 前言回顾「猫猫大作战」的按钮——「开始游戏」绿、「暂停」灰、「重新开始」红、「再来一局」绿、「返回主菜单」浅灰。每个按钮都重复写了width/height/fontSize/borderRadius这堆属性代码重复严重改一处要改 N 处。HarmonyOS 提供了AttributeModifier属性修饰器——把样式抽成可复用类一处定义、多处复用还能针对不同状态正常/按下/禁用应用不同样式。本篇以「猫猫大作战」按钮样式复用为锚点把 AttributeModifier 的定义与复用、多状态样式、与 Styles/Extend 的取舍三大要点讲透。提示本系列不讲 ArkTS 基础语法与环境搭建假设你已跟完第 1–29 篇。本篇是布局进阶的收尾篇。一、场景拆解按钮样式重复回顾「猫猫大作战」底部栏按钮第 19 篇// 来源entry/src/main/ets/pages/Index.ets GameView() 底部栏 Button(暂停) .width(100).height(44) .fontSize(15).fontColor(#FFFFFF) .backgroundColor(#95A5A6) .borderRadius(22) .onClick(() { this.pauseGame(); }) Button(重新开始) .width(100).height(44) .fontSize(15).fontColor(#FFFFFF) .backgroundColor(#E74C3C) .borderRadius(22) .onClick(() { this.clearTimers(); this.startGame(); })痛点问题表现重复代码width(100).height(44).fontSize(15)...每个按钮写一遍改一处动全身按钮高度 44 改 48要全文搜索替换多状态难管按下/禁用样式要 onTouch 手动切代码冗长AttributeModifier 的解法// 1. 定义样式类 class SecondaryButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance.width(100).height(44).fontSize(15) .fontColor(#FFFFFF).backgroundColor(#95A5A6).borderRadius(22) } } // 2. 复用 Button(暂停).attributeModifier(new SecondaryButtonModifier())关键经验AttributeModifier 把样式抽成类一处定义多处复用。二、AttributeModifier 基本用法2.1 定义样式类class PrimaryButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(70%) .height(56) .fontSize(20) .fontWeight(FontWeight.Bold) .fontColor(#FFFFFF) .backgroundColor(#2ECC71) .borderRadius(28) .shadow({ radius: 8, color: rgba(46, 204, 113, 0.4), offsetY: 4 }) } }核心要素要素说明implements AttributeModifierButtonAttribute泛型指定目标组件类型applyNormalAttribute(instance)正常状态应用样式instance.width(...)链式调用与组件属性一致2.2 应用样式Button($r(app.string.start_game)) .attributeModifier(new PrimaryButtonModifier()) .onClick(() { this.startGame(); })关键经验.attributeModifier(new XxxModifier())一行应用全套样式。2.3 三种按钮样式类// 主操作按钮绿色开始游戏 class PrimaryButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(70%).height(56) .fontSize(20).fontWeight(FontWeight.Bold) .fontColor(#FFFFFF).backgroundColor(#2ECC71) .borderRadius(28) .shadow({ radius: 8, color: rgba(46, 204, 113, 0.4), offsetY: 4 }) } } // 次要操作按钮灰色暂停 class SecondaryButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(100).height(44) .fontSize(15).fontColor(#FFFFFF) .backgroundColor(#95A5A6).borderRadius(22) } } // 危险操作按钮红色重新开始 class DangerButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(100).height(44) .fontSize(15).fontColor(#FFFFFF) .backgroundColor(#E74C3C).borderRadius(22) } }三、多状态样式3.1 applyPressedAttribute 按下状态class PrimaryButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#2ECC71) // 正常亮绿 } applyPressedAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#27AE60) // 按下深绿 } }状态回调对照回调触发时机适用applyNormalAttribute正常显示默认样式applyPressedAttribute按下时按压反馈applyDisabledAttributeenabled(false)时禁用样式applyFocusedAttribute获得焦点时键盘/遥控器焦点applySelectedAttribute选中态Toggle/Tab3.2 禁用状态样式class SubmitButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#2ECC71).fontColor(#FFFFFF) } applyDisabledAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#BDC3C7).fontColor(#ECF0F1) // 灰色禁用 } } // 使用禁用态自动应用 applyDisabledAttribute Button(提交).attributeModifier(new SubmitButtonModifier()).enabled(this.isFormValid)3.3 完整多状态按钮class FullStateButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(200).height(48) .fontSize(16).fontWeight(FontWeight.Bold) .fontColor(#FFFFFF) .backgroundColor(#2ECC71) .borderRadius(24) } applyPressedAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#27AE60) // 按下深绿 instance.scale({ x: 0.95, y: 0.95 }) // 按下缩小 5% } applyDisabledAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#BDC3C7) // 禁用灰 instance.fontColor(#ECF0F1) } }执行流程正常显示 →applyNormalAttribute用户按下 →applyPressedAttribute覆盖 normal 的差异属性用户松开 → 回到applyNormalAttributeenabled(false)→applyDisabledAttribute四、改造底部栏用 AttributeModifier4.1 定义三个按钮样式类// 次要按钮暂停 class SecondaryButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(100).height(44) .fontSize(15).fontColor(#FFFFFF) .backgroundColor(#95A5A6).borderRadius(22) } applyPressedAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#7F8C8D) // 按下深灰 } } // 危险按钮重新开始 class DangerButtonModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance .width(100).height(44) .fontSize(15).fontColor(#FFFFFF) .backgroundColor(#E74C3C).borderRadius(22) } applyPressedAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#C0392B) // 按下深红 } }4.2 改造底部栏Builder GameView() { Column() { this.GameHUD() Column() { // 预告区 Row() { /* ... */ } // 游戏棋盘 Stack() { /* 3 层叠 */ } .width(GameConfig.BOARD_WIDTH * GameConfig.CELL_SIZE) .height(GameConfig.BOARD_HEIGHT * GameConfig.CELL_SIZE) .borderRadius(12) .clip(true) .backgroundColor(#D6EEF5) } .alignItems(HorizontalAlign.Center) Spacer() // 弹性占位推底部栏到最底 // 底部控制栏AttributeModifier 改造 Row() { Button($r(app.string.pause)) .attributeModifier(new SecondaryButtonModifier()) .onClick(() { this.pauseGame(); }) Spacer() // 撑开两按钮到两端 Button($r(app.string.restart)) .attributeModifier(new DangerButtonModifier()) .onClick(() { this.clearTimers(); this.startGame(); }) } .width(100%) .padding({ left: 24, right: 24, bottom: 24, top: 12 }) } .width(100%).height(100%) .linearGradient({ direction: GradientDirection.Bottom, colors: [[#E8F4F8, 0.0], [#D6EEF5, 0.5], [#C9E8F2, 1.0]] }) .alignItems(HorizontalAlign.Center) }改造对比维度原版属性堆叠AttributeModifier 版代码量每按钮 7 行属性每按钮 1 行 modifier按压反馈无默认 stateEffectapplyPressedAttribute 自定义改样式改每个按钮的属性改 Modifier 类全应用生效复用无法复用一处定义多处用五、AttributeModifier vs Styles vs ExtendArkUI 有三种样式复用机制5.1 Styles 通用样式Styles function primaryButtonStyle() { .width(70%).height(56) .fontSize(20).fontWeight(FontWeight.Bold) } Button(开始).primaryButtonStyle()5.2 Extend 特定组件样式Extend(Button) function primaryButtonExtend() { .width(70%).height(56) .backgroundColor(#2ECC71).borderRadius(28) } Button(开始).primaryButtonExtend()5.3 三者对比机制适用组件多状态跨组件复用推荐Styles任意❌✅简单通用样式Extend指定组件❌❌特定组件样式AttributeModifier任意✅✅复杂多状态样式实战经验简单样式用 Styles特定组件用 Extend多状态/复杂复用用 AttributeModifier。5.4 选择决策树样式需要多状态按下/禁用 ├─ 是 → AttributeModifier └─ 否 └─ 样式跨多种组件复用 ├─ 是 → Styles └─ 否 └─ 样式只针对某组件 → Extend六、踩坑提示6.1 AttributeModifier 泛型不匹配// ❌ 错误Modifier 声明 ButtonAttribute但用在 Text 上 class MyModifier implements AttributeModifierButtonAttribute { /* ... */ } Text(A).attributeModifier(new MyModifier()) // 类型不匹配 // ✅ 正确泛型与组件匹配 class MyModifier implements AttributeModifierTextAttribute { /* ... */ } Text(A).attributeModifier(new MyModifier())6.2 忘记 implements AttributeModifier// ❌ 错误没 implementsattributeModifier 不接受 class MyModifier { applyNormalAttribute() {} } // ✅ 正确implements AttributeModifierT class MyModifier implements AttributeModifierButtonAttribute { /* ... */ }6.3 Modifier 与组件属性冲突// ⚠️ Modifier 设了 backgroundColor组件又设了一次后者覆盖前者 Button(A) .attributeModifier(new MyModifier()) // 设 backgroundColor(#2ECC71) .backgroundColor(#E74C3C) // 覆盖 Modifier 的绿色 // ✅ 推荐样式统一在 Modifier 里管组件只设业务属性 Button(A) .attributeModifier(new MyModifier()) .onClick(() { /* ... */ })6.4 applyPressedAttribute 没覆盖 normal 的所有差异class MyModifier implements AttributeModifierButtonAttribute { applyNormalAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#2ECC71).scale({ x: 1, y: 1 }) } applyPressedAttribute(instance: ButtonAttribute): void { instance.backgroundColor(#27AE60) // 只改了颜色scale 还是 1 // 想要按下缩小要显式设 scale instance.scale({ x: 0.95, y: 0.95 }) } }七、调试技巧临时加console.info在 applyNormalAttribute 里追样式应用时机。按压无反馈检查是否实现了applyPressedAttribute检查组件是否enabled(false)。样式不生效检查 Modifier 泛型是否与组件匹配检查是否被组件属性覆盖。多 Modifier 冲突一个组件只能用一个attributeModifier多个样式要合并到一个 Modifier 类。八、性能与最佳实践复杂多状态样式用 AttributeModifier——一处定义多处复用。泛型与组件匹配——ButtonAttribute 配 ButtonTextAttribute 配 Text。样式统一在 Modifier 管——组件只设业务属性onClick、state。简单通用样式用 Styles——AttributeModifier 杀鸡用牛刀。状态回调全覆盖——normal/pressed/disabled 都实现避免状态残留。Modifier 类可继承复用——class PrimaryModifier extends BaseModifier。九、阶段一收尾UI 组件与布局实战总结1-30本篇是阶段一「UI 组件与布局实战1-30」的收尾篇。回顾阶段一完整覆盖篇主题核心组件1-5主菜单排版Text、Button、Spacer、Column6-10主菜单视觉linearGradient、justifyContent、Row、Stack、if/else11-12游戏页 HUD 预告GameHUD、Circle 预告13-14棋盘背景ForEach 双层循环、隔行变色15-16猫咪渲染Emoji Text、Circle 背景、position 绝对定位17列点击层透明 onClick 覆盖层18-19底部控制栏Row 双按钮、Button 颜色与圆角20SymbolGlyph系统符号字体替代 Emoji21RelativeContainer扁平复杂布局22Blank替代 Spacer 的间距23layoutWeight按比例分配宽度24displayPriority窄屏自适应隐藏25Scroller长内容滚动容器26Badge消息角标27Image 资源$r 与 $rawfile28暗色模式深色资源与 colorMode29多语言 i18nstring.json 多语言30本篇AttributeModifier按钮样式复用阶段一核心收获ArkUI 声明式 UI组件 属性 事件三件套。布局容器Column/Row/Stack/RelativeContainer 四大布局。状态驱动State 改变 → build() 重渲染。资源系统$r 编译期资源 $rawfile 原始文件。多端适配暗色模式、多语言、多分辨率。样式复用Styles/Extend/AttributeModifier 三层方案。接下来阶段二31-70将进入状态管理 交互 动画深水区State/Link/Provide/Consume 状态管理、Watch 监听、setInterval 主循环、animateTo 显式动画、Reusable 列表复用、LazyForEach 大列表等。总结本篇我们从 AttributeModifier 切入掌握了样式类定义与复用、多状态样式normal/pressed/disabled、与 Styles/Extend 的取舍三大要点并给出了底部栏 AttributeModifier 改造完整代码。核心要点AttributeModifier 一处定义多处复用多状态用 applyPressedAttribute 等泛型与组件匹配。下一篇我们将进入阶段二拆解 State 单组件状态——得分驱动 UI。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源「猫猫大作战」项目源码本仓库entry/src/main/ets/pages/Index.etsAttributeModifier 属性修饰器官方指南Styles 样式复用官方指南Extend 扩展官方指南ArkUI 样式复用最佳实践开源鸿蒙跨平台社区HarmonyOS 开发者官方文档首页系列索引本仓库articles/INDEX.md

相关新闻