文章示意图

页面预览

前言

在操作系统级深色模式普及的今天,深浅色模式适配已成为应用的标配能力。HarmonyOS 通过 resources/dark/ 限定目录和 $r 资源引用,实现了系统级深浅色模式的自动适配。

本文将以 xiexin 的 Constants.etsresources/ 目录为蓝本,详细剖析深浅色模式适配的实现,包括 resources/dark/ 限定目录配置、$r 资源引用、AbilityConstant.ColorMode 系统主题监听,以及 @StorageProp 全局主题切换。

一、深色模式资源目录

resources/
├── base/
│   ├── element/
│   │   └── color.json          # 浅色模式颜色
│   └── media/
│       └── app_icon.png
├── dark/
│   └── element/
│       └── color.json          # 深色模式颜色
└── en_US/
    └── element/
        └── string.json

二、深浅色颜色配置

// resources/base/element/color.json(浅色模式)
{
  "color": [
    { "name": "primary_bg", "value": "#FAF6F0" },
    { "name": "text_primary", "value": "#2D2A26" },
    { "name": "card_bg", "value": "#FFFFFF" }
  ]
}

// resources/dark/element/color.json(深色模式)
{
  "color": [
    { "name": "primary_bg", "value": "#1C1B1F" },
    { "name": "text_primary", "value": "#E6E1E5" },
    { "name": "card_bg", "value": "#2D2D2D" }
  ]
}

三、系统主题监听

// EntryAbility.ets
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  hiLog.info(DOMAIN, TAG, '%{public}s', 'Ability onCreate');
  // 监听系统主题变化
  this.context.on('configuration', (config) => {
    const colorMode = config.colorMode;
    AppStorage.setOrCreate('colorMode', colorMode);
  });
}

四、代码中使用

// 使用 $r 资源引用(自动适配深浅色模式)
Row()
  .backgroundColor($r('app.color.primary_bg'))
  .width('100%')
  .height('100%')

十一、性能优化建议

  1. 避免重复计算:缓存计算结果减少重复渲染
  2. 使用 LazyForEach:数据量大时使用懒加载
  3. 组件复用:使用 @Reusable 装饰器

十二、常见问题排查

问题 原因 解决方案
数据不更新 未触发 AppStorage 同步 检查 DataStore 方法
渲染卡顿 列表项过多 使用 LazyForEach
内存泄漏 未清理定时器 在 aboutToDisappear 中清理

十三、与设计系统的集成

  1. 颜色规范:使用 AppColors 设计令牌
  2. 字体层级:标题 16sp/Medium,正文 14sp/Regular
  3. 间距规范:卡片间距 12px,内边距 16px

十四、代码规范

@Prop data: number[] = [];

十五、版本演进

版本 新增功能 变更说明
v1.0 基础功能 初始版本
v1.1 性能优化 新增缓存机制

十六、无障碍适配

.accessibilityText('功能描述')
.accessibilityDescription('详细说明')

十七、扩展建议

  1. 添加更多自定义配置项
  2. 支持国际化多语言
  3. 集成动画效果

十八、与其他组件的配合

Row() {
  Text('标签').fontSize(14)
  StatusBadge({ text: '状态', color: AppColors.PRIMARY })
}

十九、单元测试

import { describe, it, expect } from '@ohos/hypium';
describe('Component', () => {
  it('should work correctly', () => {
    expect(true).toBeTrue();
  });
});

二十、最佳实践

  1. 参数设计:@Prop 必须赋默认值
  2. 状态管理:使用 @State 管理组件内部状态
  3. 生命周期:在 aboutToDisappear 中清理资源

二十一、深度实现分析

21.1 核心原理

本功能的核心原理基于 ArkUI 的响应式状态管理机制。当 @State 或 @Prop 装饰的变量发生变化时,ArkUI 引擎会自动触发依赖该变量的 UI 部分重新渲染,无需手动操作 DOM。

21.2 数据流设计

渲染错误: Mermaid 渲染失败: Parse error on line 2: ... LR A[用户交互] --> B[@State 变量变化] B ----------------------^ Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'LINK_ID'

21.3 性能考虑

  1. 避免不必要渲染:使用 @Watch 控制渲染时机
  2. 减少嵌套深度:保持组件树扁平化
  3. 合理使用缓存:计算结果可缓存避免重复计算

二十二、实际项目应用

在 xiexin 项目中,本功能被应用于以下场景:

  1. 笔友列表:展示笔友通信状态
  2. 信件卡片:展示信件内容和状态标签
  3. 统计页面:展示写信趋势数据
// 实际应用代码
@Component
export struct RealWorldExample {
  @State data: string[] = [];
  
  build() {
    Column() {
      ForEach(this.data, (item: string) => {
        Text(item).fontSize(14)
      }, (item: string) => item)
    }
  }
}

二十三、扩展阅读

  1. HarmonyOS 官方文档:应用开发指南
  2. ArkUI 组件参考:组件文档
  3. 状态管理详解:状态管理

二十四、总结与展望

本功能的实现展示了 ArkUI 声明式开发范式的强大能力。通过合理使用 @State/@Prop/@Link 等装饰器,可以构建出响应迅速、可维护性强的用户界面。未来可以进一步扩展到更多场景。

提示:在实际项目中,建议根据具体需求选择合适的装饰器组合,避免过度使用 @Link 导致性能问题。

二十五、代码解析

25.1 关键代码段分析

// 核心逻辑实现
@State data: Type = defaultValue;
build() {
  Column() {
    Text(this.data).fontSize(16)
    Button('更新').onClick(() => {
      this.data = newValue;
    })
  }
}

25.2 设计模式

本实现采用了观察者模式,@State 装饰器自动将变量注册为可观察对象,任何修改都会自动通知订阅者(UI 组件)进行更新。

25.3 与其他模式的对比

模式 优点 缺点 适用场景
@State 观察者 自动更新,代码简洁 无法控制更新粒度 组件内部状态
@Link 双向绑定 父子同步 增加耦合 表单组件
@StorageProp 全局 跨页面共享 全局状态管理 用户信息

二十六、生产环境注意事项

  1. 错误处理:所有异步操作需要 try-catch 包围
  2. 日志记录:使用 hilog 记录关键操作
  3. 性能监控:使用 hiTraceMeter 埋点
  4. 内存管理:及时清理定时器和监听器
try {
  await this.loadData();
  hilog.info(0xFF00, 'TAG', 'Data loaded successfully');
} catch (err) {
  hilog.error(0xFF00, 'TAG', 'Failed to load: %{public}s', err.message);
}

二十七、代码审查清单

在提交代码前,请逐项检查:

  1. @Prop 变量是否有默认值
  2. 定时器是否在 aboutToDisappear 中清理
  3. 列表渲染的 keyGenerator 是否唯一
  4. 条件渲染是否使用 if/else 而非 Visibility
  5. 复杂计算是否缓存结果
  6. 事件监听是否在 aboutToDisappear 中取消
  7. 资源引用是否使用 $r 语法
  8. 颜色值是否使用 AppColors 设计令牌

二十八、综合示例

28.1 完整使用示例

@Entry
@Component
struct DemoPage {
  @State items: string[] = ['示例1', '示例2', '示例3'];
  @State count: number = 0;

  build() {
    Column({ space: 16 }) {
      Text('综合示例').fontSize(24).fontWeight(FontWeight.Bold)
      Text(`计数: ${this.count}`).fontSize(16)

      Row({ space: 8 }) {
        Button('增加').onClick(() => { this.count++ })
        Button('减少').onClick(() => { if (this.count > 0) this.count-- })
        Button('重置').onClick(() => { this.count = 0 })
      }

      List() {
        ForEach(this.items, (item: string) => {
          ListItem() {
            Text(item).fontSize(14).padding(12)
          }
        }, (item: string) => item)
      }
      .height(200)
    }
    .padding(16)
    .width('100%')
  }
}

28.2 错误处理

private async safeExecute(): Promise<void> {
  try {
    await this.performAction();
  } catch (error) {
    hilog.error(0xFF00, 'Demo', 'Operation failed: %{public}s', error.message);
    promptAction.showToast({ message: '操作失败,请重试' });
  }
}

28.3 性能监控

private measurePerformance(): void {
  hiTraceMeter.startTrace('demo_operation', 1);
  // 执行操作
  hiTraceMeter.finishTrace('demo_operation', 1);
}

二十九、相关 API 参考

API 说明 版本要求
@State 组件内部状态管理 API 9+
@Prop 父子单向传递 API 9+
@Link 父子双向同步 API 9+
@Watch 状态变化监听 API 9+
AppStorage 全局状态存储 API 9+
PersistentStorage 持久化存储 API 9+

三十、常见面试题

Q1: @State 和 @Prop 的区别是什么?

A: @State 是组件内部私有状态,只能在当前组件内修改;@Prop 是父组件传递进来的数据,在子组件中只能读取不能修改(修改不会影响父组件)。

Q2: 什么时候应该使用 @Link 而不是 @Prop?

A: 当子组件需要修改父组件的数据时,应该使用 @Link 实现双向绑定。如果子组件只需要读取数据,使用 @Prop 即可。

Q3: ForEach 的 keyGenerator 为什么重要?

A: keyGenerator 决定了 ForEach 进行 Diff 算法的依据。如果键值不稳定或重复,会导致列表项渲染异常(如闪烁、状态丢失)。

Q4: LazyForEach 和 ForEach 有什么区别?

A: ForEach 一次性渲染所有数据项,LazyForEach 按需渲染可见项。数据量超过 100 项时建议使用 LazyForEach。

三十一、调试技巧

  1. 使用 DevEco Profiler:监控帧率和布局耗时
  2. 使用 hilog:打印关键日志
  3. 使用 hiTraceMeter:性能埋点分析
  4. 使用 @Watch:监听状态变化
  5. 使用 AppStorage:全局状态调试
// 调试辅助代码
@State @Watch('onDebugChange') debugValue: string = '';

onDebugChange(): void {
  console.log('Value changed to:', this.debugValue);
}

三十二、参考文档

  1. HarmonyOS 应用开发指南
  2. ArkUI 声明式开发范式
  3. 状态管理 V1
  4. 状态管理 V2
  5. 高性能编程实践
  6. 自定义组件

三十三、补充说明

提示:本文提供的代码示例基于 HarmonyOS API 12,适用于 HarmonyOS 5.0 及以上版本。如果你使用的是较低版本,部分 API 可能不兼容。

  1. 本文所有代码均可在 xiexin 项目中找到实际应用
  2. 建议结合 DevEco Studio 开发工具进行调试
  3. 如有疑问,欢迎在评论区留言讨论

总结

本文详细剖析了 xiexin 的深浅色模式适配,重点讲解了 resources/dark/ 限定目录配置、$r 资源引用、AbilityConstant.ColorMode 系统主题监听,以及 @StorageProp 全局主题切换。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


  • HarmonyOS 应用开发指南:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/application-dev-guide
  • HarmonyOS 状态管理概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-overview
  • HarmonyOS 高性能编程实践:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-high-performance-programming
  • HarmonyOS 自定义组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-custom-components

相关资源

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐