• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![systemTheme](figures/systemTheme.png)
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![customTheme](figures/customTheme.gif)
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![resources_dark](figures/resources_dark.png)
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![lightDarkMode](figures/lightDarkMode.png)
308
309## 系统缺省token色值
310
311| Token                                      | 场景类别 | Light |           | Dark    |                                              |
312|--------------------------------------------|-----| --- |-----------| ------- | -------------------------------------------- |
313| theme.colors.brand                         | 品牌色 |#ff0a59f7| ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
314| theme.colors.warning                       | 一级警示色 |#ffe84026| ![](figures/ffe84026.png "#ffe84026") |#ffd94838|![](figures/ffd94838.png "#ffd94838")|
315| theme.colors.alert                         | 二级警示色 |#ffed6f21| ![](figures/ffed6f21.png "#ffed6f21") |#ffdb6b42|![](figures/ffdb6b42.png "#ffdb6b42")|
316| theme.colors.confirm                       | 确认色 |#ff64bb5c| ![](figures/ff64bb5c.png "#ff64bb5c") |#ff5ba854|![](figures/ff5ba854.png "#ff5ba854")|
317| theme.colors.fontPrimary                   | 一级文本 | #e5000000 | ![](figures/e5000000.png "#e5000000") |#e5ffffff|![](figures/e5ffffff.png "#e5ffffff")|
318| theme.colors.fontSecondary                 | 二级文本 | #99000000 | ![](figures/99000000.png "#99000000") |#99ffffff|![](figures/99ffffff.png "#99ffffff")|
319| theme.colors.fontTertiary                  | 三级文本 | #66000000 | ![](figures/66000000.png "#66000000") |#66ffffff|![](figures/66ffffff.png "#66ffffff")|
320| theme.colors.fontFourth                    | 四级文本 | #33000000 | ![](figures/33000000.png "#33000000") |#33ffffff|![](figures/33ffffff.png "#33ffffff")|
321| theme.colors.fontEmphasize                 | 高亮文本 | #ff0a59f7 | ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
322| theme.colors.fontOnPrimary                 | 一级文本反色 | #ffffffff | ![](figures/ffffffff.png "#ffffffff") |#ff000000|![](figures/ff000000.png "#ff000000")|
323| theme.colors.fontOnSecondary               | 二级文本反色 | #99ffffff | ![](figures/99ffffff.png "#99ffffff") |#99000000|![](figures/99000000.png "#99000000")|
324| theme.colors.fontOnTertiary                | 三级文本反色 | #66ffffff | ![](figures/66ffffff.png "#66ffffff") |#66000000|![](figures/66000000.png "#66000000")|
325| theme.colors.fontOnFourth                  | 四级文本反色 | #33ffffff | ![](figures/33ffffff.png "#33ffffff") |#33000000|![](figures/33000000.png "#33000000")|
326| theme.colors.iconPrimary                   | 一级图标 | #e5000000 | ![](figures/e5000000.png "#e5000000") |#e5ffffff|![](figures/e5ffffff.png "#e5ffffff")|
327| theme.colors.iconSecondary                 | 二级图标 | #99000000 | ![](figures/99000000.png "#99000000") |#99ffffff|![](figures/99ffffff.png "#99ffffff")|
328| theme.colors.iconTertiary                  | 三级图标 | #66000000 | ![](figures/66000000.png "#66000000") |#66ffffff|![](figures/66ffffff.png "#66ffffff")|
329| theme.colors.iconFourth                    | 四级图标 | #33000000 | ![](figures/33000000.png "#33000000") |#33ffffff|![](figures/33ffffff.png "#33ffffff")|
330| theme.colors.iconEmphasize                 | 高亮图标 | #ff0a59f7 | ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
331| theme.colors.iconSubEmphasize              | 高亮辅助图标 | #660a59f7 | ![](figures/660a59f7.png "#660a59f7") |#66317af7|![](figures/66317af7.png "#66317af7")|
332| theme.colors.iconOnPrimary                 | 一级图标反色 | #ffffffff | ![](figures/ffffffff.png "#ffffffff") |#ff000000|![](figures/ff000000.png "#ff000000")|
333| theme.colors.iconOnSecondary               | 二级图标反色 | #99ffffff | ![](figures/99ffffff.png "#99ffffff") |#99000000|![](figures/99000000.png "#99000000")|
334| theme.colors.iconOnTertiary                | 三级图标反色 | #66ffffff | ![](figures/66ffffff.png "#66ffffff") |#66000000|![](figures/66000000.png "#66000000")|
335| theme.colors.iconOnFourth                  | 四级图标反色 | #33ffffff | ![](figures/33ffffff.png "#33ffffff") |#33000000|![](figures/33000000.png "#33000000")|
336| theme.colors.backgroundPrimary             | 一级背景(实色/不透明色) | #ffffffff | ![](figures/ffffffff.png "#ffffffff") |#ffe5e5e5|![](figures/ffe5e5e5.png "#ffe5e5e5")|
337| theme.colors.backgroundSecondary           | 二级背景(实色/不透明色) | #fff1f3f5 | ![](figures/fff1f3f5.png "#fff1f3f5") |#ff191a1c|![](figures/ff191a1c.png "#ff191a1c")|
338| theme.colors.backgroundTertiary            | 三级背景(实色/不透明色) | #ffe5e5ea | ![](figures/ffe5e5ea.png "#ffe5e5ea") |#ff202224|![](figures/ff202224.png "#ff202224")|
339| theme.colors.backgroundFourth              | 四级背景(实色/不透明色) | #ffd1d1d6 | ![](figures/ffd1d1d6.png "#ffd1d1d6") |#ff2e3033|![](figures/ff2e3033.png "#ff2e3033")|
340| theme.colors.backgroundEmphasize           | 高亮背景(实色/不透明色) | #ff0a59f7 | ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
341| theme.colors.compForegroundPrimary         | 前背景 | #ff000000 | ![](figures/ff000000.png "#ff000000") | #ffe5e5e5 |![](figures/ffe5e5e5.png "#ffe5e5e5")|
342| theme.colors.compBackgroundPrimary         | 白色背景 | #ffffffff |![](figures/ffffffff.png "#ffffffff")| #ffffffff |![](figures/ffffffff.png "#ffffffff")|
343| theme.colors.compBackgroundPrimaryTran     | 白色透明背景 | #ffffffff |![](figures/ffffffff.png "#ffffffff")| #33ffffff |![](figures/33ffffff.png "#33ffffff")|
344| theme.colors.compBackgroundPrimaryContrary | 常亮背景 | #ffffffff |![](figures/ffffffff.png "#ffffffff")| #ffe5e5e5 |![](figures/ffe5e5e5.png "#ffe5e5e5")|
345| theme.colors.compBackgroundGray            | 灰色背景 | #fff1f3f5 |![](figures/fff1f3f5.png "#fff1f3f5")| #ffe5e5ea |![](figures/ffe5e5ea.png "#ffe5e5ea")|
346| theme.colors.compBackgroundSecondary       | 二级背景 | #19000000 |![](figures/19000000.png "#19000000")| #19ffffff |![](figures/19ffffff.png "#19ffffff")|
347| theme.colors.compBackgroundTertiary        | 三级背景 | #0c000000 |![](figures/0c000000.png "#0c000000")| #0cffffff |![](figures/0cffffff.png "#0cffffff")|
348| theme.colors.compBackgroundEmphasize       | 高亮背景 | #ff0a59f7 |![](figures/ff0a59f7.png "#ff0a59f7")| #ff317af7 |![](figures/ff317af7.png "#ff317af7")|
349| theme.colors.compBackgroundNeutral         | 黑色中性高亮背景 | #ff000000 |![](figures/ff000000.png "#ff000000")| #ffffffff |![](figures/ffffffff.png "#ffffffff")|
350| theme.colors.compEmphasizeSecondary        | 20%高亮背景 | #330a59f7 |![](figures/330a59f7.png "#330a59f7")| #33317af7 |![](figures/33317af7.png "#33317af7")|
351| theme.colors.compEmphasizeTertiary         | 10%高亮背景 | #190a59f7 |![](figures/190a59f7.png "#190a59f7")| #19317af7 |![](figures/19317af7.png "#19317af7")|
352| theme.colors.compDivider                   | 分割线颜色 | #33000000 |![](figures/33000000.png "#33000000")| #33ffffff |![](figures/33ffffff.png "#33ffffff")|
353| theme.colors.compCommonContrary            | 通用反色 | #ffffffff |![](figures/ffffffff.png "#ffffffff")| #ff000000 |![](figures/ff000000.png "#ff000000")|
354| theme.colors.compBackgroundFocus           | 获焦态背景色 | #fff1f3f5 |![](figures/fff1f3f5.png "#fff1f3f5")| #ff000000 |![](figures/fff1f3f5.png "#fff1f3f5")|
355| theme.colors.compFocusedPrimary            | 获焦态一级反色 | #e5000000 |![](figures/e5000000.png "#e5000000")| #e5ffffff |![](figures/e5ffffff.png "#e5ffffff")|
356| theme.colors.compFocusedSecondary          | 获焦态二级反色 | #99000000 |![](figures/99000000.png "#99000000")| #99ffffff |![](figures/99ffffff.png "#99ffffff")|
357| theme.colors.compFocusedTertiary           | 获焦态三级反色 | #66000000 |![](figures/66000000.png "#66000000")| #66ffffff |![](figures/66ffffff.png "#66ffffff")|
358| theme.colors.interactiveHover              | 通用悬停交互式颜色 | #0c000000 |![](figures/0c000000.png "#0c000000")| #0cffffff |![](figures/0cffffff.png "#0cffffff")|
359| theme.colors.interactivePressed            | 通用按压交互式颜色 | #19000000 |![](figures/19000000.png "#19000000")| #19ffffff |![](figures/19ffffff.png "#19ffffff")|
360| theme.colors.interactiveFocus              | 通用获焦交互式颜色 | #ff0a59f7 |![](figures/ff0a59f7.png "#ff0a59f7")| #ff317af7 |![](figures/ff317af7.png "#ff317af7")|
361| theme.colors.interactiveActive             | 通用激活交互式颜色 | #ff0a59f7 |![](figures/ff0a59f7.png "#ff0a59f7")| #ff317af7 |![](figures/ff317af7.png "#ff317af7")|
362| theme.colors.interactiveSelect             | 通用选择交互式颜色 | #33000000 |![](figures/33000000.png "#33000000")| #33ffffff |![](figures/33ffffff.png "#33ffffff")|
363| theme.colors.interactiveClick              | 通用点击交互式颜色 | #19000000 |![](figures/19000000.png "#19000000")| #19ffffff |![](figures/19ffffff.png "#19ffffff")|
364