1# 设置主题换肤 2 3## 概述 4 5对于使用ets声明式前端开发的应用,提供应用内组件的主题能力,支持局部深浅色、动态换肤功能。本功能不支持C-API和Node-API,不支持Ability和窗口的主题设置。 6本文提供如下场景: 7- [自定义品牌色](#自定义品牌色) 8- [应用级自定义品牌色](#设置应用内组件自定义品牌色) 9- [局部页面自定义主题风格](#设置应用局部页面自定义主题风格) 10- [局部深浅色](#设置应用页面局部深浅色) 11 12 13## 自定义品牌色 14[CustomTheme](../reference/apis-arkui/js-apis-arkui-theme.md#customtheme)用于自定义主题,属性可选,只需要复写修改的部分,未修改内容继承于系统,参考[系统缺省token色值](#系统缺省token色值)。请参考: 15 16 ```ts 17 import { CustomColors, CustomTheme } from '@kit.ArkUI' 18 19 export class AppColors implements CustomColors { 20 //自定义品牌色 21 brand: ResourceColor = '#FF75D9'; 22 } 23 24 export class AppTheme implements CustomTheme { 25 public colors: AppColors = new AppColors() 26 } 27 28 export let gAppTheme: CustomTheme = new AppTheme() 29 ``` 30 31## 设置应用内组件自定义品牌色 32- 可在页面入口处统一设置,需要在页面build前执行[ThemeControl](../reference/apis-arkui/js-apis-arkui-theme.md#themecontrol)。 33其中,[onWillApplyTheme](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onwillapplytheme12)回调函数用于自定义组件获取当前生效的Theme对象。 34 35 ```ts 36 import { Theme, ThemeControl } from '@kit.ArkUI' 37 import { gAppTheme } from './AppTheme' 38 39 //在页面build前执行ThemeControl 40 ThemeControl.setDefaultTheme(gAppTheme) 41 42 @Entry 43 @Component 44 struct DisplayPage { 45 @State menuItemColor: ResourceColor = $r('sys.color.background_primary') 46 47 onWillApplyTheme(theme: Theme) { 48 this.menuItemColor = theme.colors.backgroundPrimary; 49 } 50 51 build() { 52 Column() { 53 List({ space: 10 }) { 54 ListItem() { 55 Column({ space: '5vp' }) { 56 Text('Color mode') 57 .margin({ top: '5vp', left: '14fp' }) 58 .width('100%') 59 Row() { 60 Column() { 61 Text('Light') 62 .fontSize('16fp') 63 .textAlign(TextAlign.Start) 64 .alignSelf(ItemAlign.Center) 65 Radio({ group: 'light or dark', value: 'light' }) 66 .checked(true) 67 } 68 .width('50%') 69 70 Column() { 71 Text('Dark') 72 .fontSize('16fp') 73 .textAlign(TextAlign.Start) 74 .alignSelf(ItemAlign.Center) 75 Radio({ group: 'light or dark', value: 'dark' }) 76 } 77 .width('50%') 78 } 79 } 80 .width('100%') 81 .height('90vp') 82 .borderRadius('10vp') 83 .backgroundColor(this.menuItemColor) 84 } 85 86 ListItem() { 87 Column() { 88 Text('Brightness') 89 .width('100%') 90 .margin({ top: '5vp', left: '14fp' }) 91 Slider({ value: 40, max: 100 }) 92 } 93 .width('100%') 94 .height('70vp') 95 .borderRadius('10vp') 96 .backgroundColor(this.menuItemColor) 97 } 98 99 ListItem() { 100 Column() { 101 Row() { 102 Column({ space: '5vp' }) { 103 Text('Touch sensitivity') 104 .fontSize('16fp') 105 .textAlign(TextAlign.Start) 106 .width('100%') 107 Text('Increase the touch sensitivity of your screen' + 108 ' for use with screen protectors') 109 .fontSize('12fp') 110 .fontColor(Color.Blue) 111 .textAlign(TextAlign.Start) 112 .width('100%') 113 } 114 .alignSelf(ItemAlign.Center) 115 .margin({ left: '14fp' }) 116 .width('75%') 117 118 Toggle({ type: ToggleType.Switch, isOn: true }) 119 .margin({ right: '14fp' }) 120 .alignSelf(ItemAlign.Center) 121 } 122 .width('100%') 123 .height('80vp') 124 } 125 .width('100%') 126 .borderRadius('10vp') 127 .backgroundColor(this.menuItemColor) 128 } 129 } 130 } 131 .padding('10vp') 132 .backgroundColor('#dcdcdc') 133 .width('100%') 134 .height('100%') 135 } 136 } 137 ``` 138 139- 在Ability中设置[ThemeControl](../reference/apis-arkui/js-apis-arkui-theme.md#themecontrol),需要在onWindowStageCreate()方法中[setDefaultTheme](../reference/apis-arkui/js-apis-arkui-theme.md#setdefaulttheme)。 140 141 ```ts 142 import {AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 143 import { hilog } from '@kit.PerformanceAnalysisKit'; 144 import { window, CustomColors, ThemeControl } from '@kit.ArkUI'; 145 146 class AppColors implements CustomColors { 147 fontPrimary = 0xFFD53032 148 iconOnPrimary = 0xFFD53032 149 iconFourth = 0xFFD53032 150 } 151 152 const abilityThemeColors = new AppColors(); 153 154 export default class EntryAbility extends UIAbility { 155 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 156 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 157 } 158 159 onDestroy() { 160 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 161 } 162 163 onWindowStageCreate(windowStage: window.WindowStage) { 164 // Main window is created, set main page for this ability 165 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 166 167 windowStage.loadContent('pages/Index', (err, data) => { 168 if (err.code) { 169 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 170 return; 171 } 172 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 173 // 在onWindowStageCreate()方法中setDefaultTheme 174 ThemeControl.setDefaultTheme({ colors: abilityThemeColors }) 175 hilog.info(0x0000, 'testTag', '%{public}s', 'ThemeControl.setDefaultTheme done'); 176 }); 177 } 178 179 } 180 ``` 181 182 183 184注:如果setDefaultTheme的参数为undefined时,默认token值对应的色值参考[系统缺省token色值](#系统缺省token色值)。 185 186## 设置应用局部页面自定义主题风格 187将自定义Theme的配色通过设置[WithTheme](../reference/apis-arkui/arkui-ts/ts-container-with-theme.md#withetheme)作用于内组件缺省样式,WithTheme作用域内组件配色跟随Theme的配色生效。 188在下面示例中,通过WithTheme({ theme: this.myTheme })将作用域内的组件配色设置为自定义主题风格。后续可通过更改this.myTheme更换主题风格。 189[onWillApplyTheme](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onwillapplytheme12)回调函数用于自定义组件获取当前生效的Theme对象。 190 191 ```ts 192 import { CustomColors, CustomTheme, Theme } from '@kit.ArkUI' 193 194 class AppColors implements CustomColors { 195 fontPrimary: ResourceColor = $r('app.color.brand_purple') 196 backgroundEmphasize: ResourceColor = $r('app.color.brand_purple') 197 } 198 199 class AppColorsSec implements CustomColors { 200 fontPrimary: ResourceColor = $r('app.color.brand') 201 backgroundEmphasize: ResourceColor = $r('app.color.brand') 202 } 203 204 class AppTheme implements CustomTheme { 205 public colors: AppColors = new AppColors() 206 } 207 208 class AppThemeSec implements CustomTheme { 209 public colors: AppColors = new AppColorsSec() 210 } 211 212 @Entry 213 @Component 214 struct DisplayPage { 215 @State customTheme: CustomTheme = new AppTheme() 216 @State message: string = '设置应用局部页面自定义主题风格' 217 count = 0; 218 219 build() { 220 WithTheme({ theme: this.customTheme }) { 221 Row(){ 222 Column() { 223 Text('WithTheme') 224 .fontSize(30) 225 .margin({bottom: 10}) 226 Text(this.message) 227 .margin({bottom: 10}) 228 Button('change theme').onClick(() => { 229 this.count++; 230 if (this.count > 1) { 231 this.count = 0; 232 } 233 switch (this.count) { 234 case 0: 235 this.customTheme = new AppTheme(); 236 break; 237 case 1: 238 this.customTheme = new AppThemeSec(); 239 break; 240 } 241 }) 242 } 243 .width('100%') 244 } 245 .height('100%') 246 .width('100%') 247 } 248 } 249 } 250 ``` 251 252 253 254## 设置应用页面局部深浅色 255通过[WithTheme](../reference/apis-arkui/arkui-ts/ts-container-with-theme.md#withetheme)可以设置深浅色模式,[ThemeColorMode](../reference/apis-arkui/arkui-ts/ts-container-with-theme.md#themecolormode10枚举说明)有三种模式,ThemeColorMode.SYSTEM模式表示跟随系统模式,ThemeColorMode.LIGHT模式表示浅色模式,ThemeColorMode.DARK模式表示深色模式。</br> 256在WithTheme作用域内,组件的样式资源取值跟随指定的模式读取对应的深浅色模式系统和应用资源值,WithTheme作用域内的组件配色跟随指定的深浅模式生效。</br> 257在下面的示例中,通过WithTheme({ colorMode: ThemeColorMode.DARK })将作用域内的组件设置为深色模式。 258 259设置局部深浅色时,需要添加dark.json资源文件,深浅色模式才会生效。 260 261 262 263dark.json数据示例: 264 ```ts 265 { 266 "color": [ 267 { 268 "name": "start_window_background", 269 "value": "#FFFFFF" 270 } 271 ] 272 } 273 ``` 274 275 ```ts 276 @Entry 277 @Component 278 struct DisplayPage { 279 @State message: string = 'Hello World'; 280 @State colorMode: ThemeColorMode = ThemeColorMode.DARK; 281 282 build() { 283 WithTheme({ colorMode: this.colorMode }) { 284 Row() { 285 Column() { 286 Text(this.message) 287 .fontSize(50) 288 .fontWeight(FontWeight.Bold) 289 Button('Switch ColorMode').onClick(() => { 290 if (this.colorMode === ThemeColorMode.LIGHT) { 291 this.colorMode = ThemeColorMode.DARK; 292 } else if (this.colorMode === ThemeColorMode.DARK) { 293 this.colorMode = ThemeColorMode.LIGHT; 294 } 295 }) 296 } 297 .width('100%') 298 } 299 .backgroundColor($r('sys.color.background_primary')) 300 .height('100%') 301 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.END, SafeAreaEdge.BOTTOM, SafeAreaEdge.START]) 302 } 303 } 304 } 305 ``` 306 307 308 309## 系统缺省token色值 310 311| Token | 场景类别 | Light | | Dark | | 312|--------------------------------------------|-----| --- |-----------| ------- | -------------------------------------------- | 313| theme.colors.brand | 品牌色 |#ff0a59f7|  |#ff317af7|| 314| theme.colors.warning | 一级警示色 |#ffe84026|  |#ffd94838|| 315| theme.colors.alert | 二级警示色 |#ffed6f21|  |#ffdb6b42|| 316| theme.colors.confirm | 确认色 |#ff64bb5c|  |#ff5ba854|| 317| theme.colors.fontPrimary | 一级文本 | #e5000000 |  |#e5ffffff|| 318| theme.colors.fontSecondary | 二级文本 | #99000000 |  |#99ffffff|| 319| theme.colors.fontTertiary | 三级文本 | #66000000 |  |#66ffffff|| 320| theme.colors.fontFourth | 四级文本 | #33000000 |  |#33ffffff|| 321| theme.colors.fontEmphasize | 高亮文本 | #ff0a59f7 |  |#ff317af7|| 322| theme.colors.fontOnPrimary | 一级文本反色 | #ffffffff |  |#ff000000|| 323| theme.colors.fontOnSecondary | 二级文本反色 | #99ffffff |  |#99000000|| 324| theme.colors.fontOnTertiary | 三级文本反色 | #66ffffff |  |#66000000|| 325| theme.colors.fontOnFourth | 四级文本反色 | #33ffffff |  |#33000000|| 326| theme.colors.iconPrimary | 一级图标 | #e5000000 |  |#e5ffffff|| 327| theme.colors.iconSecondary | 二级图标 | #99000000 |  |#99ffffff|| 328| theme.colors.iconTertiary | 三级图标 | #66000000 |  |#66ffffff|| 329| theme.colors.iconFourth | 四级图标 | #33000000 |  |#33ffffff|| 330| theme.colors.iconEmphasize | 高亮图标 | #ff0a59f7 |  |#ff317af7|| 331| theme.colors.iconSubEmphasize | 高亮辅助图标 | #660a59f7 |  |#66317af7|| 332| theme.colors.iconOnPrimary | 一级图标反色 | #ffffffff |  |#ff000000|| 333| theme.colors.iconOnSecondary | 二级图标反色 | #99ffffff |  |#99000000|| 334| theme.colors.iconOnTertiary | 三级图标反色 | #66ffffff |  |#66000000|| 335| theme.colors.iconOnFourth | 四级图标反色 | #33ffffff |  |#33000000|| 336| theme.colors.backgroundPrimary | 一级背景(实色/不透明色) | #ffffffff |  |#ffe5e5e5|| 337| theme.colors.backgroundSecondary | 二级背景(实色/不透明色) | #fff1f3f5 |  |#ff191a1c|| 338| theme.colors.backgroundTertiary | 三级背景(实色/不透明色) | #ffe5e5ea |  |#ff202224|| 339| theme.colors.backgroundFourth | 四级背景(实色/不透明色) | #ffd1d1d6 |  |#ff2e3033|| 340| theme.colors.backgroundEmphasize | 高亮背景(实色/不透明色) | #ff0a59f7 |  |#ff317af7|| 341| theme.colors.compForegroundPrimary | 前背景 | #ff000000 |  | #ffe5e5e5 || 342| theme.colors.compBackgroundPrimary | 白色背景 | #ffffffff || #ffffffff || 343| theme.colors.compBackgroundPrimaryTran | 白色透明背景 | #ffffffff || #33ffffff || 344| theme.colors.compBackgroundPrimaryContrary | 常亮背景 | #ffffffff || #ffe5e5e5 || 345| theme.colors.compBackgroundGray | 灰色背景 | #fff1f3f5 || #ffe5e5ea || 346| theme.colors.compBackgroundSecondary | 二级背景 | #19000000 || #19ffffff || 347| theme.colors.compBackgroundTertiary | 三级背景 | #0c000000 || #0cffffff || 348| theme.colors.compBackgroundEmphasize | 高亮背景 | #ff0a59f7 || #ff317af7 || 349| theme.colors.compBackgroundNeutral | 黑色中性高亮背景 | #ff000000 || #ffffffff || 350| theme.colors.compEmphasizeSecondary | 20%高亮背景 | #330a59f7 || #33317af7 || 351| theme.colors.compEmphasizeTertiary | 10%高亮背景 | #190a59f7 || #19317af7 || 352| theme.colors.compDivider | 分割线颜色 | #33000000 || #33ffffff || 353| theme.colors.compCommonContrary | 通用反色 | #ffffffff || #ff000000 || 354| theme.colors.compBackgroundFocus | 获焦态背景色 | #fff1f3f5 || #ff000000 || 355| theme.colors.compFocusedPrimary | 获焦态一级反色 | #e5000000 || #e5ffffff || 356| theme.colors.compFocusedSecondary | 获焦态二级反色 | #99000000 || #99ffffff || 357| theme.colors.compFocusedTertiary | 获焦态三级反色 | #66000000 || #66ffffff || 358| theme.colors.interactiveHover | 通用悬停交互式颜色 | #0c000000 || #0cffffff || 359| theme.colors.interactivePressed | 通用按压交互式颜色 | #19000000 || #19ffffff || 360| theme.colors.interactiveFocus | 通用获焦交互式颜色 | #ff0a59f7 || #ff317af7 || 361| theme.colors.interactiveActive | 通用激活交互式颜色 | #ff0a59f7 || #ff317af7 || 362| theme.colors.interactiveSelect | 通用选择交互式颜色 | #33000000 || #33ffffff || 363| theme.colors.interactiveClick | 通用点击交互式颜色 | #19000000 || #19ffffff || 364