HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本

HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本 【标题】 示例代码适配状态管理V2版本

【设备信息】

【API版本】Api14

【DevEco Studio版本】 5.0.5.315

【问题描述】
我项目中使用了状态管理V2的三方库,但是当前深色模式的示例代码使用的状态管理V2,有大佬知道该如何将该代码适配状态管理V2吗

【问题相关代码】

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-light-dark-color-adaptation-V5

这篇文档底部的示例代码


更多关于HarmonyOS 鸿蒙Next监听系统深色模式示例代码的状态管理V2版本的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

主要就是适配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中有效地管理深色模式的切换状态。

回到顶部