文化知识应用的插画通常承担路线、地域、神兽与进度提示等信息。只替换页面背景色会让深色界面的山水纹理、金色强调色和卡片文本彼此争夺注意力;反过来,浅色界面也容易因透明图层过多而丢失边界。山海万灵把颜色、插画和组件表面拆成同一套主题分支,让主题切换直接驱动页面外壳、内容卡片与背景资源更新。

浅色主题下的山海首页

上图的浅色首页保留了山水路线、地域节点、统计卡和底部导航。卡片使用浅色表面和偏深文字,路线与“继续探索”按钮使用独立的强调色,避免在大面积插画上把所有信息都压成同一层。

把主题值收敛成可组合的令牌

页面不直接散落判断式颜色。ShanhaiTheme 为背景、卡片、边框、主次文本、金色强调与地图路线提供成对值,组件只根据 isDarkMode 读取语义化令牌。这样新增页面时只选择它需要的角色,不必重新发明一组深浅色常量。

static readonly PAGE_BG: string = '#07171C';
static readonly DARK_CARD_SURFACE: string = '#03171D';
static readonly DARK_CARD_RAISED: string = '#0A252C';
static readonly DARK_CARD_SELECTED: string = '#16444A';
static readonly LINE: string = '#2F6268';
static readonly GOLD: string = '#D8A84E';
static readonly CREAM: string = '#F7EBD3';
static readonly SUB_TEXT: string = '#B7CACB';

static readonly LIGHT_PAGE_BG: string = '#F6F0E4';
static readonly LIGHT_PANEL_BG: string = '#FFF9ED';
static readonly LIGHT_PANEL_BG_ALT: string = '#EAF1E4';
static readonly LIGHT_TAG_BG: string = '#E2EAD9';
static readonly LIGHT_LINE: string = '#C8B98F';
static readonly LIGHT_GOLD: string = '#A56F20';
static readonly LIGHT_CREAM: string = '#26372E';
static readonly LIGHT_SUB_TEXT: string = '#5F6D64';

static pageBg(isDarkMode: boolean): string {
  return isDarkMode ? ShanhaiTheme.PAGE_BG : ShanhaiTheme.LIGHT_PAGE_BG;
}

static panelBg(isDarkMode: boolean): string {
  return isDarkMode ? ShanhaiTheme.PANEL_BG : ShanhaiTheme.LIGHT_PANEL_BG;
}

static textPrimary(isDarkMode: boolean): string {
  return isDarkMode ? ShanhaiTheme.CREAM : ShanhaiTheme.LIGHT_CREAM;
}

这种分层解决了两个常见问题:一是把“深色”误当作所有区域都用纯黑;二是让正文、辅助说明和禁用态共用同一种灰色。山海万灵的深色页面背景、主卡片、抬升卡片和输入表面分别对应不同深度,浅色页面也为卡片、替代表面和标签保留独立值。

视觉角色 深色主题 浅色主题 使用位置
页面外壳 深青黑 米白 整页背景与空白区域
内容卡片 深色半透明表面 暖白表面 统计、路线与阅读信息
强调信息 金色与青绿色 褐金与青绿色 进度、路线、主操作
次级文字 低饱和浅青 灰绿色 说明、计数和辅助状态

主题状态从页面入口向下传递

主题状态由首页统一持有。用户点击页头的主题控制后,页面状态发生一次明确更新;首页内容、图鉴、探索、馆长和“我的”模块接收相同的 isDarkMode,避免局部组件自行缓存旧主题。

ShanhaiHomePageContent({
  beasts: this.beasts,
  discoveredCount: this.discoveredIds.length,
  passportCount: this.passportRegionIds.length,
  isDarkMode: this.isDarkMode,
  onToggleTheme: () => {
    this.isDarkMode = !this.isDarkMode;
  }
})

显式传递比在每个子组件中再次读取全局状态更容易检查。组件的颜色、边框和资源选择都依赖同一布尔值,切换时不会出现标题已变色而背景仍停留在旧图的分裂状态。对有选中态的地域卡和护照印章墙,主题与业务选中状态保持独立:切换外观不会重置发现记录、当前路线或阅读上下文。

页头的图标和点击区域同样接收主题状态。太阳与月亮资源随状态切换,透明圆形表面使用不同透明度;点击事件只通知页面入口更新状态,组件本身不保存第二份主题副本。

private themeToggleIcon(): Resource {
  return this.isDarkMode
    ? $r('app.media.shanhai_toggle_moon_v2')
    : $r('app.media.shanhai_toggle_sun_v2');
}

Stack() {
  Image(this.themeToggleIcon())
    .width(46)
    .height(46)
    .objectFit(ImageFit.Contain)
}
.width(54)
.height(54)
.backgroundColor(this.isDarkMode ? '#0CF7EBD3' : '#44FFFFFF')
.borderRadius(27)
.onClick(() => {
  this.onToggleTheme();
})

插画资源也要成对选择

文化插画不是普通装饰。首页和展厅使用带有山水、云气和神兽的背景,它们的明暗关系决定了文本能否被读到。组件根据主题和宽度选择对应资源,再让主题令牌负责文字与卡片层级。

private panelAsset(): Resource {
  return this.isDarkMode
    ? $r('app.media.shanhai_content_panel_dark_v4')
    : $r('app.media.shanhai_content_panel_light_v4');
}

.backgroundImage(shanhaiPageBackdropAsset(
  this.isDarkMode,
  this.isWideScreen
))

这里有两个边界。第一,深色资源不是在浅色图片上简单加黑蒙层;月色、云纹和远山需要保留可辨识的亮度范围。第二,业务内容不能烘焙在插画里。统计数、地域名称、路线进度和按钮仍由 ArkUI 文本与组件绘制,因此主题切换、数据刷新和多设备布局不会破坏可访问性。

深色主题下的山海首页

深色主题下,背景改为夜色山水和月纹,统计卡、金色数字、路线节点与底部导航仍可分辨。切换前后内容数据、地域路线和统计值保持不变,变化只发生在主题令牌与对应的插画资源分支。

容易被忽略的层级问题

深色模式的回归不只检查“有没有换色”。下面三类问题最容易在文化插画页面出现:

问题 表现 处理方式
文本被纹理吞没 地域名、说明和统计标签难以阅读 主次文本分别取主题令牌,卡片与插画之间保留不透明或半透明表面
选中态不明显 地图节点和底部导航无法区分当前项 选中背景、边框和金色强调同时参与状态表达,不只改变文字颜色
图片与布局脱节 宽屏下背景裁切后遮住内容 背景资源选择同时接收主题和宽度,内容区域仍按自适应间距布局

资源缺失时,页面应先保留主题颜色、标题、统计和导航等可操作信息,再使用安全的普通表面作为背景;不把一张加载失败的插画当成全页不可用。网络同步失败也不应改变用户已经选定的主题,页面可以提示数据来源状态,同时继续用本地内容完成阅读和探索。

验收:一次切换要同时检查哪些区域

验证时,从首页启动后依次观察页头、统计卡、路线图、主操作和底部导航;点击主题控制切换一次,再确认以下结果:

  1. 页面背景、山水插画和月亮/日间视觉随主题同时变化。
  2. 标题、说明、金色统计值、地域标签和路线节点仍保持足够对比度。
  3. 已发现数量、护照印章、知识节点和当前路线不因切换而改变。
  4. 底部导航的当前项与未选中项仍有可区分的颜色和表面层级。
  5. 返回其他一级页面后,卡片、边框和插画继续使用同一主题状态。

统一的主题令牌把颜色选择收拢在可维护的入口,成对插画资源解决文化画面在不同亮度下的阅读问题,显式状态传递则保证首页与各功能模块在一次主题切换后保持同步。更多 ArkUI 状态管理方式可参考 HarmonyOS 官方状态管理文档

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐