OpenHarmony 项目统一全局样式、尺寸、色彩主题封装 ThemeUtil(API23)

📅 2026/7/21 15:06:20
OpenHarmony 项目统一全局样式、尺寸、色彩主题封装 ThemeUtil(API23)
摘要项目中文字大小、颜色、圆角、间距、按钮尺寸、阴影等样式值分散写在各个页面出现多处硬编码 #007DFF、fontSize (18)、padding (12)后期产品统一换肤、调整主色调需要全局逐行修改维护成本极高。封装全局主题常量 ThemeUtil统一管理主色 / 辅助色 / 文字色、间距、圆角、字体大小、通用阴影支持一键切换深色 / 浅色两套主题所有页面、组件统一引用主题变量一键全局换色。API23 优化资源常量响应式同步、主题状态联动 AppStorage修复深色模式切换页面局部样式不刷新、颜色硬编码无法批量替换、组件样式重复冗余等问题。关键词OpenHarmonyArkTS主题封装全局色彩尺寸常量深色浅色切换样式统一Theme一、引言1.1 原生硬编码样式开发痛点颜色、字号、间距分散在上千行页面代码改版换色需全局检索替换极易遗漏浅色、深色两套页面颜色手动分别写切换时大量判断分支无统一规范有的页面间距 10有的 12UI 视觉割裂不统一按钮、卡片、弹窗圆角大小各处不一致缺乏设计规范约束无法统一管理阴影、边框、分割线等通用样式新增页面需要反复复制颜色值开发效率低。1.2 主题分层结构色彩体系主色、辅助色、成功 / 警告 / 错误色、一级 / 二级 / 占位文字、分割线、背景色区分深浅两套间距体系小间距、标准间距、大间距、超大边距圆角体系小圆角、标准卡片圆角、大弹窗圆角、全圆角按钮字号体系标题、正文、辅助小字、提示文字通用样式卡片阴影、输入框边框、分割线API23 主题相关升级支持主题常量配合 GlobalStore 全局深色状态自动动态取值样式常量可全局缓存减少重复创建资源对象组件 Builder 样式提取复用减少重复布局代码修复深色模式切换后部分组件颜色不实时刷新问题。二、全局主题常量 model/theme.etsetsimport GlobalStore from ../utils/global_store import { GLOBAL_KEY } from ./GlobalState // 尺寸、圆角、间距 固定常量深浅共用 export const SizeConstant { // 间距 gapXs: 4, gapSm: 8, gapMd: 12, gapLg: 16, gapXl: 24, pagePadding: 12, // 圆角 radiusXs: 4, radiusSm: 8, radiusMd: 12, radiusLg: 16, radiusFull: 999, // 字号 fontTip: 12, fontAux: 14, fontMain: 16, fontSubTitle: 18, fontTitle: 22, fontBigTitle: 26, // 通用控件高度 btnHeightSm: 36, btnHeightMd: 44, inputHeight: 48 } // 浅色主题色彩 const LightColor { primary: #007DFF, success: #00B42A, warning: #FF7D00, danger: #F53F3F, text1: #1D2129, text2: #4E5969, text3: #86909C, textPlaceholder: #C9CDD4, bgPage: #F5F5F5, bgCard: #FFFFFF, line: #E5E6EB } // 深色主题色彩 const DarkColor { primary: #36A3FF, success: #37D05B, warning: #FF9500, danger: #FF5C5C, text1: #F2F3F5, text2: #C9CDD4, text3: #86909C, textPlaceholder: #4E5969, bgPage: #121212, bgCard: #1E1E1E, line: #333333 } // 主题统一出口自动判断深浅模式 export function ThemeColor() { const isDark GlobalStore.get(GLOBAL_KEY.DARK_MODE) return isDark ? DarkColor : LightColor } // 通用卡片阴影样式 export function CardShadow() { const color ThemeColor().line return { shadowColor: color, shadowBlur: 6, shadowOffsetX: 0, shadowOffsetY: 2 } }三、主题工具辅助封装 utils/theme_util.etsetsimport { SizeConstant, ThemeColor, CardShadow } from ../model/theme class ThemeUtil { private static instance: ThemeUtil static getInstance() { if (!ThemeUtil.instance) ThemeUtil.instance new ThemeUtil() return ThemeUtil.instance } // 获取当前整套色彩 getColor() { return ThemeColor() } // 获取尺寸常量 getSize() { return SizeConstant } // 获取卡片阴影 getCardShadow() { return CardShadow() } // 快速生成标准卡片通用修饰符 getCardCommon() { const size this.getSize() const color this.getColor() return { borderRadius: size.radiusMd, backgroundColor: color.bgCard, padding: size.gapMd, shadow: this.getCardShadow() } } } export default ThemeUtil.getInstance()四、页面实战全局主题调用示例4.1 笔记列表卡片页面完整使用etsimport ThemeUtil from ../utils/theme_util import RefreshListView from ../components/common/RefreshListView import StateView, { PageState } from ../components/common/StateView import { Note } from ../utils/rdb_util Entry Component struct NoteListPage { State pageState: PageState PageState.LOADING State noteList: Note[] [] build() { const theme ThemeUtil.getColor() const size ThemeUtil.getSize() const cardStyle ThemeUtil.getCardCommon() Column({ space: size.gapMd }) { Row() { Text(我的笔记) .fontSize(size.fontTitle) .fontColor(theme.text1) .layoutWeight(1) Button(新增笔记) .height(size.btnHeightMd) .borderRadius(size.radiusFull) .backgroundColor(theme.primary) } .width(95%) StateView({ state: this.pageState }) { RefreshListView({ list: this.noteList }) { (item: Note) { Row() .width(100%) .padding(cardStyle.padding) .borderRadius(cardStyle.borderRadius) .backgroundColor(cardStyle.backgroundColor) .shadow(cardStyle.shadow) { Column({ space: size.gapXs }).layoutWeight(1) { Text(item.title) .fontSize(size.fontMain) .fontColor(theme.text1) Text(item.content) .fontSize(size.fontAux) .fontColor(theme.text3) } Button(删除) .height(size.btnHeightSm) .backgroundColor(theme.danger) .borderRadius(size.radiusSm) } } } } } .width(100%) .height(100%) .padding(size.pagePadding) .backgroundColor(theme.bgPage) } }4.2 设置页深色模式切换实时变色etsimport GlobalStore from ../utils/global_store import ThemeUtil from ../utils/theme_util Entry Component struct SettingPage { State dark: boolean false aboutToAppear() { this.dark GlobalStore.get(GLOBAL_KEY.DARK_MODE) } build() { const color ThemeUtil.getColor() const size ThemeUtil.getSize() Column() .width(100%) .height(100%) .padding(size.pagePadding) .backgroundColor(color.bgPage) { Row() .width(100%) .padding(size.gapLg) .borderRadius(size.radiusMd) .backgroundColor(color.bgCard) { Text(深色模式) .fontSize(size.fontMain) .fontColor(color.text1) .layoutWeight(1) Toggle({ isOn: this.dark }) .onChange((v) GlobalStore.toggleDarkMode(v)) } } } }五、通用业务组件规范改造StateView 示例etsComponent export struct StateView { Param state: PageState PageState.LOADING BuilderParam contentBuilder: () void Param onRetry: () void State rotateAngle: number 0 build() { const color ThemeUtil.getColor() const size ThemeUtil.getSize() Column() { if (this.state PageState.LOADING) { Column({ space: size.gapMd }) { Text(⟳).fontSize(42).rotate({ angle: this.rotateAngle }) Text(加载中...) .fontSize(size.fontAux) .fontColor(color.text3) } } else if (this.state PageState.EMPTY) { Column({ space: size.gapMd }) { Image($r(sys.media.ohos_ic_public_empty)).width(80).fillColor(color.text3) Text(暂无数据) .fontSize(size.fontAux) .fontColor(color.text3) } } else if (this.state PageState.ERROR) { Column({ space: size.gapLg }) { Image($r(sys.media.ohos_ic_public_fail)).width(80).fillColor(color.text3) Text(加载失败请点击重试) .fontSize(size.fontAux) .fontColor(color.text3) Button(重试) .width(140) .height(size.btnHeightMd) .backgroundColor(color.primary) .borderRadius(size.radiusSm) .onClick(this.onRetry) } } else { this.contentBuilder() } } .width(100%) .layoutWeight(1) } }六、主题开发编码规范6.1 常量分层规范尺寸 / 圆角 / 字号写入 SizeConstant深浅模式共用固定数值统一设计规范色彩分 LightColor / DarkColor 两套通过 ThemeColor () 动态获取复合样式卡片阴影、通用卡片修饰符封装成函数多处复用。6.2 页面使用规范页面 build 顶部统一解构const color ThemeUtil.getColor(); const size ThemeUtil.getSize()禁止页面直接写 #xxxxxx、12、16 等硬编码数字与色值页面背景、卡片背景、文字颜色全部使用主题变量深浅自动切换。6.3 换肤维护规范产品改版仅修改 theme.ets 内主色值全局页面自动同步新增业务色如会员紫、积分橙统一在 Light/Dark 两套色彩同步新增统一设计尺寸修改仅调整 SizeConstant 一处全部页面间距、字号同步变化。6.4 深色模式联动规范ThemeColor () 内部读取 GlobalStore 深色全局状态无需页面额外判断切换 Toggle 修改全局 darkMode 后页面 State 自动刷新颜色实时切换弹窗、列表、按钮等公共组件全部接入主题深浅无视觉断层。6.5 组件复用规范所有公共基础组件StateView、RefreshListView、Dialog 弹窗全部接入 ThemeUtil禁止组件内部写死颜色、尺寸完全依赖主题入参。七、高频问题解决方案问题 1切换深色模式页面颜色不刷新 解决页面 UI 依赖 ThemeColor () 动态函数取值搭配 GlobalStore 响应式状态自动触发重绘。问题 2改版主色调需要修改上百处代码 解决全部页面引用主题常量仅修改 theme.ets 中 primary 字段全局生效。问题 3各页面卡片圆角、间距大小不统一 解决强制所有页面使用 SizeConstant 内固定尺寸禁止手写数字。问题 4深色模式分割线、背景发白刺眼 解决分开维护 LightColor 与 DarkColor 两套独立背景 / 分割线色值不共用颜色。问题 5按钮高度、输入框尺寸到处不一致 解决统一使用 btnHeightMd、inputHeight 尺寸常量约束控件高度。八、总结ThemeUtil 全局主题体系分离固定尺寸常量与深浅两套动态色彩统一管控项目全部视觉规范彻底消除颜色、字号、间距硬编码实现一键全局换肤、深浅模式自动适配。 所有页面、公共 UI 组件统一接入主题工具和前文 GlobalStore 全局深色状态联动一套规范贯穿整个 HAR/HSP 分层工程极大降低 UI 改版、设计规范调整的维护成本是标准化企业级鸿蒙项目必备视觉底层封装。