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