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