HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本
HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本 【标题】 示例代码适配状态管理V2版本
【设备信息】
【API版本】Api14
【DevEco Studio版本】 5.0.5.315
【问题描述】
我项目中使用了状态管理V2的三方库,但是当前深色模式的示例代码使用的状态管理V2,有大佬知道该如何将该代码适配状态管理V2吗
【问题相关代码】
这篇文档底部的示例代码
更多关于HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本的实战教程也可以访问 https://www.itying.com/category-93-b0.html
主要就是适配AppStorage到AppStorageV2,可以参考如下几个文件的修改:
EntryAbility.ets
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AppStorageV2, window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { AbilityConstant, Configuration, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { SampleHeight } from '../pages/Index';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
const currentColorMode = AppStorageV2.connect<SampleClass>(
SampleClass, "currentColorMode",
() => new SampleClass(this.context.config.colorMode)
);
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
const windowClass = windowStage.getMainWindowSync();
windowClass.setWindowLayoutFullScreen(true);
const avoidAreaTop = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const avoidAreaBottom = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
const topRectHeight = avoidAreaTop.topRect.height;
const bottomRectHeight = avoidAreaBottom.bottomRect.height;
const a: SampleHeight = AppStorageV2.connect<SampleHeight>(
SampleHeight, "topRectHeight",
() => new SampleHeight(px2vp(topRectHeight))
)!;
const b: SampleHeight = AppStorageV2.connect<SampleHeight>(
SampleHeight, "bottomRectHeight",
() => new SampleHeight(px2vp(bottomRectHeight))
)!;
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
onConfigurationUpdate(newConfig: Configuration): void {
const currentColorMode = AppStorageV2.connect<SampleClass>(
SampleClass, "currentColorMode",
() => new SampleClass(newConfig.colorMode)
);
if (currentColorMode?.p !== newConfig.colorMode) {
const colorModeConfig = AppStorageV2.connect<SampleClass>(
SampleClass, "currentColorMode",
() => new SampleClass(newConfig.colorMode)
);
}
}
}
@ObservedV2
export class SampleClass {
@Trace p: ConfigurationConstant.ColorMode | undefined;
constructor(p: ConfigurationConstant.ColorMode | undefined) {
this.p = p;
}
}
DarkModeSetting.ets
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { common } from '@kit.AbilityKit';
import { SampleClass } from '../entryability/EntryAbility';
import { setDarkColorMode, setLightColorMode, setAutoColorMode } from '../viewmodel/ColorModeChangeFunctions';
import { AppStorageV2 } from '@kit.ArkUI';
@ComponentV2
export struct DarkModeSetting {
@Local enableDarkMode: SampleBoolean = AppStorageV2.connect<SampleBoolean>(
SampleBoolean, "enableDarkMode",
() => new SampleBoolean(true)
)!;
@Local isFollowSystemSetting: SampleBoolean = AppStorageV2.connect<SampleBoolean>(
SampleBoolean, "isFollowSystemSetting",
() => new SampleBoolean(true)
)!;
@Local currentColorMode: SampleClass | undefined = AppStorageV2.connect<SampleClass>(
SampleClass, "currentColorMode",
() => new SampleClass(0)
);
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
aboutToDisappear(): void {
const p1 = AppStorageV2.connect<SampleBoolean>(
SampleBoolean, "enableDarkMode",
() => new SampleBoolean(this.enableDarkMode.p)
);
const p2 = AppStorageV2.connect<SampleBoolean>(
SampleBoolean, "isFollowSystemSetting",
() => new SampleBoolean(this.isFollowSystemSetting.p)
);
}
build() {
NavDestination() {
Column() {
Text($r('app.string.follow_system_settings_switch_mode'))
.fontColor($r('app.color.sub_text_color'))
.fontSize(14)
.fontWeight(400)
.lineHeight(20)
.margin({ bottom: 16, top: 8 })
.width('100%')
.textAlign(TextAlign.Start)
Row() {
Text($r('app.string.follow_system_settings'))
.fontColor($r('app.color.font_color'))
.fontSize(16)
.fontWeight(500)
.lineHeight(22)
Toggle({ type: ToggleType.Switch, isOn: this.isFollowSystemSetting.p })
.onChange((isOn: boolean) => {
if (isOn) {
this.isFollowSystemSetting.p = true;
this.enableDarkMode.p = false;
setAutoColorMode(this.context);
} else {
this.isFollowSystemSetting.p = false;
if (this.enableDarkMode.p) {
setDarkColorMode(this.context);
} else {
setLightColorMode(this.context);
}
}
})
}
.width('100%')
.height(56)
.backgroundColor($r('app.color.item_box'))
.margin({ bottom: 12 })
.padding({ left: 12, right: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.borderRadius(12)
Row() {
Text($r('app.string.dark_mode'))
.fontColor($r('app.color.font_color'))
.fontSize(16)
.fontWeight(500)
.lineHeight(22)
Toggle({ type: ToggleType.Switch, isOn: this.enableDarkMode.p })
.onChange((isOn: boolean) => {
this.enableDarkMode.p = isOn;
if (isOn) {
this.isFollowSystemSetting.p = false;
setDarkColorMode(this.context);
} else if (!this.isFollowSystemSetting.p) {
setLightColorMode(this.context);
}
})
}
.width('100%')
.height(56)
.backgroundColor($r('app.color.item_box'))
.padding({ left: 12, right: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.borderRadius(12)
}
.padding({ left: 16, right: 16 })
}
.title('Dark Mode')
.backgroundColor($r('app.color.app_background_color'))
}
}
@ObservedV2
export class SampleBoolean {
@Trace p: boolean = true;
constructor(p: boolean) {
this.p = p;
}
}
Home.ets
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Todo } from '../viewmodel/Todo';
import { TodoItem } from '../view/TodoItem';
import { FinishedTodoItem } from '../view/FinishedTodoItem';
import { TodoViewModel } from '../viewmodel/TodoViewModel';
import { AppStorageV2 } from '@kit.ArkUI';
import { SampleClass } from '../entryability/EntryAbility';
@ComponentV2
export struct Home {
@Local todos: Todo[] = new TodoViewModel().getTodos();
@Local currentColorMode: SampleClass | undefined = AppStorageV2.connect<SampleClass>(
SampleClass, "currentColorMode",
() => new SampleClass(0)
);
build() {
Scroll() {
Column() {
Row() {
Row() {
SymbolGlyph($r('sys.symbol.person_fill'))
.fontSize(24)
.fontColor([$r('app.color.left_icon_color')])
}
.width(40)
.height(40)
.backgroundColor($r('app.color.icon_bg_color'))
.borderRadius(20)
.justifyContent(FlexAlign.Center)
Search({ placeholder: $r('app.string.solution') })
.layoutWeight(1)
.margin({ left: 8, right: 8 })
.placeholderColor(
this.currentColorMode?.p === 0 ?
$r('sys.color.font_on_secondary') :
$r('sys.color.font_secondary')
)
.searchIcon({
color:
this.currentColorMode?.p === 0 ?
$r('sys.color.icon_on_primary') :
$r('sys.color.icon_primary')
})
Row() {
SymbolGlyph($r('sys.symbol.dot_grid_2x2'))
.fontSize(24)
.fontColor([$r('app.color.right_icon_color')])
}
.width(40)
.height(40)
.backgroundColor($r('app.color.icon_bg_color'))
.borderRadius(20)
.justifyContent(FlexAlign.Center)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 11 })
Stack({ alignContent: Alignment.TopStart }) {
Image($r('app.media.banner'))
.width('100%')
.borderRadius(12)
.objectFit(ImageFit.Cover)
Column() {
Text($r('app.string.2d_subscene_solution'))
.fontColor(Color.White)
.fontSize(24)
.fontWeight(700)
.lineHeight(32)
.textAlign(TextAlign.Start)
Text($r('app.string.latest_news'))
.fontColor(Color.White)
.fontSize(24)
.fontWeight(700)
.lineHeight(32)
.textAlign(TextAlign.Start)
Text($r('app.string.multiple_solutions'))
.fontColor('#99FFFFFF')
.fontSize(12)
.fontWeight(400)
.lineHeight(16)
.textAlign(TextAlign.Start)
}
.width(300)
.margin({ left: 16, top: 16 })
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.height(200)
.borderRadius(12)
.margin({ bottom: 13 })
Row() {
Text($r('app.string.today'))
.fontColor($r('app.color.font_color'))
.fontSize(16)
.fontWeight(400)
.lineHeight(21)
.margin({ right: 8 })
Text($r('app.string.lunar_september_fourth'))
.fontColor($r('app.color.todo_date'))
.fontSize(12)
.fontWeight(400)
.lineHeight(16)
}
.width('100%')
.height(25)
.padding({ left: 2 })
.margin({ bottom: 8 })
Swiper() {
ForEach(this.todos, (todo: Todo) => {
Column() {
TodoItem({ todo })
}
.height(108)
}, (todo: Todo) => todo.id)
}
.autoPlay(true)
.loop(true)
.indicator(new DotIndicator()
.itemWidth(8)
.itemHeight(8)
.selectedItemWidth(12)
.selectedItemHeight(8)
.color($r('sys.color.comp_background_secondary'))
.selectedColor($r('sys.color.comp_background_emphasize'))
.maxDisplayCount(7)
)
Row() {
Text($r('app.string.all'))
.fontColor($r('app.color.font_color'))
.fontSize(16)
.fontWeight(400)
.lineHeight(21)
.margin({ right: 8 })
Text($r('app.string.lunar_september_fourth'))
.fontColor($r('app.color.todo_date'))
.fontSize(12)
.fontWeight(400)
.lineHeight(16)
}
.width('100%')
.height(25)
.padding({ left: 2 })
.margin({ top: 12, bottom: 8 })
List({ space: 8 }) {
ListItem() {
FinishedTodoItem({ todo: this.todos[0] })
}
ForEach(this.todos, (todo: Todo) => {
ListItem() {
TodoItem({ todo })
}
}, (todo: Todo) => todo.id)
}
.width('100%')
Row() {
}
.width('100%')
.height(76)
.backgroundColor(Color.Transparent)
}
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 16 })
}
}
Index.ets
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DarkModeSetting } from './DarkModeSetting';
import { Home } from './Home';
import { Mine } from './Mine';
import { AppStorageV2 } from '@kit.ArkUI';
@Entry
@ComponentV2
struct Index {
@Local currentIndex: number = 0;
@Provider() navPathStack: NavPathStack = new NavPathStack();
@Local topRectHeight: SampleHeight = AppStorageV2.connect<SampleHeight>(
SampleHeight, "topRectHeight",
() => new SampleHeight(0)
)!;
@Local bottomRectHeight: SampleHeight = AppStorageV2.connect<SampleHeight>(
SampleHeight, "bottomRectHeight",
() => new SampleHeight(0)
)!;
private tabsController: TabsController = new TabsController();
@Builder
tabBuilder(title: ResourceStr, unSelectIcon: Resource, selectedIcon: Resource, targetIndex: number) {
Column() {
SymbolGlyph(this.currentIndex === targetIndex ? selectedIcon : unSelectIcon)
.fontSize(24)
.fontColor(this.currentIndex === targetIndex ?
[$r('sys.color.icon_emphasize')] :
[$r('sys.color.icon_secondary')]
)
.symbolEffect(new BounceSymbolEffect(EffectScope.WHOLE, EffectDirection.UP), this.currentIndex === targetIndex)
.margin({ bottom: 4 })
Text(title)
.fontSize(10)
.fontWeight(500)
.lineHeight(13)
.fontColor(this.currentIndex === targetIndex ?
$r('sys.color.icon_emphasize') :
$r('sys.color.icon_secondary')
)
}
.width('100%')
.height(52 + this.bottomRectHeight.p)
.padding({ top: 5 })
.justifyContent(FlexAlign.Start)
.onClick(() => {
this.currentIndex = targetIndex;
this.tabsController.changeIndex(targetIndex);
})
}
@Builder
PageMap(name: string) {
if ('darkModeSetting' === name) {
DarkModeSetting()
}
}
build() {
Navigation(this.navPathStack) {
Tabs({ controller: this.tabsController }) {
TabContent() {
Home()
}
.tabBar(this.tabBuilder(
$r('app.string.home'),
$r('sys.symbol.house'),
$r('sys.symbol.house_fill'),
0
))
TabContent() {
}
.tabBar(this.tabBuilder(
$r('app.string.discover'),
$r('sys.symbol.fast'),
$r('sys.symbol.fast_fill'),
1
))
TabContent() {
Mine()
}
.tabBar(this.tabBuilder(
$r('app.string.mine'),
$r('sys.symbol.person'),
$r('sys.symbol.person_fill'),
2
))
}
.barPosition(BarPosition.End)
.scrollable(false)
.barHeight(52 + this.bottomRectHeight.p)
.barOverlap(true)
.barBackgroundColor('#1AE6E6E6')
.barBackgroundBlurStyle(BlurStyle.COMPONENT_THICK)
}
.backgroundColor($r('app.color.app_background_color'))
.hideTitleBar(true)
.navDestination(this.PageMap)
.padding({ top: this.topRectHeight.p })
}
}
@ObservedV2
export class SampleHeight {
@Trace p: number;
constructor(p: number) {
this.p = p;
}
}
Mine.ets
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@ComponentV2
export struct Mine {
@Consumer('navPathStack') navPathStack: NavPathStack = new NavPathStack();
build() {
Column() {
Text($r('app.string.my_account'))
.height(40)
.fontColor($r('app.color.font_color'))
.fontSize(30)
.fontWeight(700)
.lineHeight(40)
Row() {
Image($r('app.media.circle'))
.width(40)
.height(40)
.borderRadius(20)
Column() {
Text($r('app.string.xiaoyi'))
.fontColor($r('app.color.font_color'))
.fontSize(16)
.fontWeight(500)
.lineHeight(22)
Text('123456789')
.fontColor($r('app.color.sub_text_color'))
.fontSize(14)
.fontWeight(400)
.lineHeight(18)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.margin({ left: 12 })
SymbolGlyph($r('sys.symbol.chevron_right'))
.fontSize(24)
.fontColor([$r('app.color.arrow_color')])
}
.width('100%')
.height(80)
.borderRadius(16)
.backgroundColor($r('app.color.item_box'))
.padding(12)
.margin({ top: 20, bottom: 12 })
Row() {
Text($r('app.string.dark_mode'))
.fontColor($r('app.color.font_color'))
.fontSize(16)
.fontWeight(500)
.lineHeight(22)
Row() {
Text($r('app.string.follow_system'))
.fontColor($r('app.color.sub_text_color'))
.fontSize(14)
.fontWeight(400)
.lineHeight(20)
SymbolGlyph($r('sys.symbol.chevron_right'))
.fontSize(24)
.fontColor([$r('app.color.arrow_color')])
}
.alignItems(VerticalAlign.Center)
}
.width('100%')
.height(56)
.padding(12)
.borderRadius(16)
.backgroundColor($r('app.color.item_box'))
.justifyContent(FlexAlign.SpaceBetween)
.onClick(() => {
this.navPathStack.pushPathByName('darkModeSetting', null)
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Start)
.padding({ left: 16, right: 16, top: 64 })
}
}
更多关于HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
大佬牛逼!!!多谢,
在HarmonyOS鸿蒙Next中,监听系统深色模式的状态管理V2版本可以通过使用ohos.app.ability.Configuration
类来实现。以下是一个示例代码,展示了如何在鸿蒙系统中监听深色模式的变化并进行状态管理。
import Configuration from '@ohos.app.ability.Configuration';
import common from '@ohos.app.ability.common';
class DarkModeManager {
private context: common.UIAbilityContext;
private currentConfig: Configuration;
constructor(context: common.UIAbilityContext) {
this.context = context;
this.currentConfig = this.context.config;
this.context.on('configurationChange', this.onConfigurationChange.bind(this));
}
private onConfigurationChange(newConfig: Configuration) {
if (this.currentConfig.colorMode !== newConfig.colorMode) {
this.currentConfig = newConfig;
this.handleDarkModeChange(newConfig.colorMode);
}
}
private handleDarkModeChange(colorMode: Configuration.ColorMode) {
if (colorMode === Configuration.ColorMode.COLOR_MODE_DARK) {
console.log('深色模式已启用');
} else {
console.log('浅色模式已启用');
}
}
}
// 在UIAbility中使用
export default class MainAbility extends Ability {
private darkModeManager: DarkModeManager;
onCreate(want, launchParam) {
this.darkModeManager = new DarkModeManager(this.context);
}
}
在这个示例中,DarkModeManager
类负责监听系统配置的变化,特别是colorMode
的变化。当系统切换为深色模式或浅色模式时,handleDarkModeChange
方法会被调用,并根据当前的colorMode
执行相应的操作。
通过这种方式,你可以在鸿蒙Next中有效地管理深色模式的切换状态。