HarmonyOS鸿蒙Next中Ability如何响应系统深色模式切换而不重启?

HarmonyOS鸿蒙Next中Ability如何响应系统深色模式切换而不重启? 用户在设置中切换主题,我的 App 页面重建了,导致正在编辑的内容丢失。能不能只换颜色不重建页面?

4 回复

开发者你好,

可尝试以下深色模式切换,编辑内容不会丢失,如果不能满足,可提供具体复现demo,以便分析。

【背景知识】

  • ComponentContent:ComponentContent表示组件内容的实体封装,其对象支持在非UI组件中创建与传递,便于开发者对弹窗类组件进行解耦封装。ComponentContent底层使用了BuilderNode,相关使用规格参考BuilderNode
  • updateConfiguration:传递系统环境变化事件,触发节点的全量更新。

【解决方案】

  1. 使用ApplicationContext.setColorMode()方法存储当前颜色模式;
  2. 通过@Watch监听onColorModeChange()方法中的深浅模式的切换;
  3. 调用updateConfiguration()方法触发节点更新;
  4. 最后执行onConfigurationUpdate()方法更新存储中的当前颜色模式。

详情请参考如下代码:

// Index.ets
import { ComponentContent, window } from '@kit.ArkUI'; 
import { customDialogBuilder } from './TestView';
import { ConfigurationConstant } from '@kit.AbilityKit'; 

const componentContentMap: Array<ComponentContent<[Object]>> = new Array(); // 初始化组件内容映射数组

@Entry
@Component
struct Index {
  @StorageProp('currentColorMode') [@Watch](/user/Watch)('onColorModeChange') currentMode: number =
    ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT; // 监听当前颜色模式并初始化为LIGHT模式
  // 监听颜色模式变化
  onColorModeChange(): void {
    componentContentMap.forEach((value, index) => {
      // 更新每个组件内容的配置
      value.updateConfiguration();
    });
  }

  build() {
    Row() {
      Column({ space: 20 }) {
        Button('打开自定义弹窗')
          .fontSize(20)
          .onClick(async () => {
            const lastWindow = await window.getLastWindow(getHostContext());
            const uiContext = lastWindow.getUIContext();
            // 创建一个新的组件内容对象
            const componentContent = new ComponentContent(
              uiContext,
              wrapBuilder(customDialogBuilder)
            );
            // 将新的组件内容添加到映射数组中
            componentContentMap.push(componentContent);
            uiContext.getPromptAction().openCustomDialog(componentContent);
          });
      }
      .width('100%')
    }
    .height('100%')
  }
}
// EntryAbility.ets
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    // 设置存储中的当前颜色模式
    AppStorage.setOrCreate('currentColorMode', this.context.config.colorMode);
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onConfigurationUpdate(newConfig: Configuration): void {
     // 更新存储中的当前颜色模式
    AppStorage.setOrCreate('currentColorMode', newConfig.colorMode);
    hilog.info(0x0000, 'testTag', 'the newConfig.colorMode is %{public}s',
      JSON.stringify(AppStorage.get('currentColorMode')) ?? '');
  }
// TestView.ets
@Builder
export function customDialogBuilder() {
  customDialogComponent()
}

@Component
struct customDialogComponent {
  build() {
    Column() {
      Text('深浅模式测试')
        .fontSize(30)
        .fontColor($r('app.color.fontColor'))
    }
    .height(200)
    .padding(5)
    .justifyContent(FlexAlign.SpaceBetween)
    .backgroundColor($r('app.color.backColor'))
  }
}

更多关于HarmonyOS鸿蒙Next中Ability如何响应系统深色模式切换而不重启?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,Ability可通过监听Configuration变化来响应深色模式切换。在onConfigurationUpdate(config: Configuration)回调中,检测colorMode属性变化。当colorModeConfigurationConstant.ColorMode.COLOR_MODE_DARK时表示深色模式,COLOR_MODE_LIGHT为浅色模式。在此回调中更新UI资源即可实现动态切换,无需重启Ability。

在HarmonyOS Next中,Ability(特别是UIAbility)默认会在系统主题(如深色/浅色模式)切换时重建,这是系统为保证资源正确加载而设计的机制。要避免页面重建导致的数据丢失,关键在于将UI与数据分离,并利用状态管理和资源动态加载。

核心方案如下:

  1. 使用UI状态与数据持久化:将页面的临时数据(如输入框内容)通过AppStorageLocalStorage等状态管理工具进行持久化存储。当Ability因主题切换重建时,UI可以从这些存储中恢复数据,而不是丢失。
  2. 监听系统主题变化并动态应用:在UI代码中,通过window.getLastWindow(ctx)获取窗口对象,并监听'colorSchemeChange'事件。当系统主题切换时,此事件会触发,你可以在事件回调中动态更新UI的颜色资源,而无需重建页面。
    • 示例:在aboutToAppear生命周期中订阅事件,在aboutToDisappear中取消订阅。
    • 在事件回调中,使用ResourceManager重新获取当前主题下的颜色值,并更新到UI组件的状态变量中,驱动UI刷新。
  3. 使用资源引用和动态颜色:在布局中,使用资源引用(如$r('app.color.my_text_color'))或绑定到状态变量,而不是硬编码颜色值。当主题切换时,系统会自动为资源引用加载对应的颜色值,或通过你更新的状态变量触发UI重新渲染。

简要步骤示例(ArkTS)

// 1. 使用AppStorage存储临时数据(如输入内容)
AppStorage.SetOrCreate('inputText', '');

// 2. 在UI组件中监听主题变化
import window from '@ohos.window';

@Entry
@Component
struct MyPage {
  @State currentColor: Resource = $r('app.color.my_color'); // 绑定颜色资源
  private windowObj: window.Window | null = null;

  aboutToAppear() {
    // 获取窗口并监听主题切换
    window.getLastWindow(this.context).then((win) => {
      this.windowObj = win;
      this.windowObj.on('colorSchemeChange', () => {
        // 主题切换时,重新加载颜色资源并更新状态
        this.currentColor = $r('app.color.my_color'); // 重新获取资源
        // 其他UI状态更新...
      });
    });
  }

  aboutToDisappear() {
    if (this.windowObj) {
      this.windowObj.off('colorSchemeChange'); // 取消监听
    }
  }

  build() {
    Column() {
      // 使用动态颜色
      Text('Hello').fontColor(this.currentColor)
      // 输入框绑定到AppStorage中的数据
      TextInput({ text: AppStorage.Get('inputText') })
        .onChange((value) => {
          AppStorage.Set('inputText', value);
        })
    }
  }
}

总结:通过状态持久化保存数据,结合监听系统主题事件动态更新UI资源,即可实现Ability在深色/浅色模式切换时仅更新颜色而不重建页面,避免数据丢失。注意,UIAbility本身可能仍会经历生命周期,但页面状态和数据可以完全恢复。

回到顶部