前言

沉浸光感放在独立实验页面里时,问题通常比较单纯。组件数量不多,背景也容易控制,某个效果出现异常以后,顺着 ImmersiveMaterial 和几个参数往下检查,通常能够找到原因。

真正进入已有项目以后,情况会复杂很多。一张已经稳定运行的业务卡片,可能同时保留背景色、圆角、阴影和背景模糊;列表里几十个 Item 又复用了同一套组件;页面滚动时还伴随着透明度和位移动画;底部如果继续接入 HDS,设备材质能力和材质等级也会参与最终结果。

这些配置分开使用时都很正常,叠到同一个页面以后,就容易互相影响。我自己前面遇到类似问题时,也会下意识继续调材质参数。效果太淡,就换 ImmersiveStyle;颜色太实,就修改 materialColor;阴影不明显,又开始调整 .shadow()。后来发现,这样排查很容易越调越乱,因为真正的问题有时还停留在普通背景属性这一层。

现在我更习惯先固定一个顺序。先看普通背景和模糊有没有影响材质,再看 materialColor、阴影和自动反色条件。组件本身已经稳定以后,再把注意力放到长列表、动画频率和设备能力上。这样从局部到页面逐层检查,问题会更容易收窄。

性能判断也应该放在后面。一个卡片已经被普通背景覆盖时,继续讨论帧率和设备分档没有太大价值。前面的显示关系处理清楚,后面的性能边界才有意义。

这次实验页面会故意保留三种状态:一张相对干净的推荐组合、一张属性顺序存在干扰的卡片,以及一张使用完全不透明 materialColor 的卡片。页面还会读取当前 MaterialState 和 HDS 材质类型,把属性冲突、设备能力和降级策略放进同一条排查路径。

目前我的测试设备还没有 HarmonyOS 7 实机测试权限,因此当前先通过模拟器核对接口、属性关系和页面结构。帧率、发热、不同算力设备上的材质细节以及真实动画手感,仍然需要在具备权限的真机上继续确认。华为也明确区分模拟器与真机调试环境,部分图形显示和硬件能力存在差异。

一、显示效果出现异常时先排查属性关系

沉浸材质出现异常以后,我现在通常不会马上去换 THINREGULAR 或其他样式。更值得先确认的,是当前组件原来已经配置了哪些视觉属性。

沉浸式系统材质本身会参与背景色、边框、阴影以及材质滤镜等显示过程。HarmonyOS 当前的沉浸光感指南也把这些属性纳入材质影响范围,并给出了先完成普通样式设置、随后再调用 systemMaterial 的推荐写法。

所以,看到页面表现不符合预期时,我们可以先按照下面的顺序检查:

普通背景与模糊
↓
沉浸材质参数
↓
前景文字与图标
↓
列表、动画等页面结构
↓
设备能力与降级策略

这个顺序对我比较有用,因为越靠前的问题,通常越容易确认。

背景色已经把材质层盖住时,没有必要继续研究设备等级;materialColor 已经接近完全不透明时,也没有必要继续比较不同 ImmersiveStyle。把这些基础关系整理清楚,后续排查才不会反复绕回来。

普通背景和背景模糊先检查

已有项目接入沉浸材质时,很容易把原来的背景配置一起保留下来。

例如:

Column() {
  Text('冲突示例')
}
.width('88%')
.height(108)
.borderRadius(24)
.systemMaterial(this.thinMaterial)
.backgroundColor(Color.White)

这里已经设置了 systemMaterial,后面又继续增加不透明背景色。

在诊断页面中,我会故意保留这种写法,让它和正常组合放在一起比较。不过到了正式项目,通常先把组件恢复到更简单的状态:

Column() {
  Text('推荐组合')
}
.width('88%')
.height(108)
.borderRadius(24)
.systemMaterial(this.thinMaterial)

如果原组件里还存在 backgroundBlurStyle、普通边框或其他背景效果,也可以先暂时移除。

当前阶段只需要确认一件事情:系统材质本身能否稳定显示。

材质恢复以后,再把业务真正需要的样式逐项加回来。这样会多操作几次,后面却能清楚知道是哪一个属性开始改变最终结果。官方同样采用先配置尺寸、圆角等属性,随后设置 systemMaterial 的方式。

materialColor 变成纯色块时先看透明度

背景属性没有明显干扰以后,下一个很常见的问题来自 materialColor

例如:

private readonly opaqueMaterial: uiMaterial.Material =
  new uiMaterial.ImmersiveMaterial({
    style: uiMaterial.ImmersiveStyle.THIN,
    materialColor: '#FFFFFFFF'
  });

代码本身能够正常创建材质,画面却可能越来越接近普通白色卡片。

这里的问题来自赋色透明度。

materialColor 会继续参与材质层颜色处理。完全不透明以后,底层背景信息会被大幅压掉,材质原本的通透感也很难继续观察。大家注意,材质赋色需要保留透明度。

页面只需要稍微稳定背景时,可以改成:

private readonly tintedMaterial: uiMaterial.Material =
  new uiMaterial.ImmersiveMaterial({
    style: uiMaterial.ImmersiveStyle.THIN,
    materialColor: '#4DFFFFFF'
  });

我自己更习惯从较低的 alpha 开始调。背景仍然可以辨认,同时卡片色调已经趋于统一,这个程度往往已经够用。继续提高不透明度以后,页面虽然更稳定,沉浸材质的空间层次也会随之减弱。

因此,卡片突然接近纯色时,materialColor 往往比 ImmersiveStyle 更值得优先检查。

阴影需要先明确由哪一套规则负责

背景和赋色确认以后,阴影是另一类经常让人绕进去的问题。

ImmersiveOptions 本身提供 applyShadow,默认值为 true。项目中的组件如果又存在自己的 .shadow(),最好明确最终希望保留哪一套视觉策略。

当前诊断页面不需要把阴影问题做得过于复杂。正式项目已经有统一阴影规范时,可以单独准备一份关闭材质阴影的配置,再观察组件原有阴影是否符合设计要求:

private readonly customShadowMaterial:
  uiMaterial.Material =
    new uiMaterial.ImmersiveMaterial({
      style: uiMaterial.ImmersiveStyle.THIN,
      applyShadow: false
    });

随后仍然由组件设置自己的阴影:

Column() {
  Text('自定义阴影')
}
.width('88%')
.height(108)
.borderRadius(24)
.shadow({
  radius: 16,
  color: '#33000000',
  offsetX: 0,
  offsetY: 6
})
.systemMaterial(this.customShadowMaterial)

这里真正需要留下的是一条清楚的规则。

项目选择系统材质阴影,就让系统材质负责;项目已经有统一的阴影体系,就在同一设备上确认关闭材质阴影后的实际表现,再决定是否沿用原来的 .shadow()。两套阴影都长期留在页面里,后面调整起来会越来越难解释。

自动反色没有明显变化时继续检查它的前提

普通背景、赋色和阴影都没有问题以后,前景颜色才进入排查范围。

colorInvert 已经打开,文字仍然没有明显变化时,可以继续沿着下面的条件往下检查:

THIN / ULTRA_THIN
↓
前景属性是否位于支持范围
↓
颜色资源是否满足自动反色条件
↓
系统沉浸光感设置是否一致
↓
当前设备是否能够呈现对应效果

这里没有必要继续增加新的文字颜色做随机尝试。

自动反色有自己明确的触发条件,先确认条件会更容易找到原因。沉浸光感会根据设备算力和系统中的光感强度调整最终效果,不同设备上的表现本身也可能存在差异。

前面这几层都确认以后,单个组件已经比较稳定。接下来出现滚动卡顿、动画掉帧或者设备之间差异明显时,才真正进入性能问题。

二、属性关系稳定以后再判断页面的性能边界

单张卡片运行正常,并不代表整个页面也会一直保持同样的状态。

页面进入长列表以后,同时存在的节点数量会增加;加入拖动、滚动显隐和展开动画以后,状态更新频率也会提升;再换到不同设备上,材质本身还会根据设备能力调整表现。

到了这个阶段,排查重点需要从一个组件扩展到整张页面。我通常先看材质到底出现了多少次,随后检查高频动画怎么写,最后再回到真机确认设备差异。这样的顺序比一开始就盯着帧率数字更容易找到真正的优化空间。

长列表里先决定哪些位置真正需要材质

资讯、商品、联系人这类页面很容易出现一个情况:页面整体开始使用沉浸光感以后,每一个 ListItem 也顺手加上材质。视觉上未必因此更清楚,页面里的材质节点却会持续增加。

例如一个几十条数据的列表,可以先尝试这样的结构:

顶部搜索框
→ 沉浸材质

底部操作区域
→ 沉浸材质

普通 ListItem
→ 普通背景

这已经能够形成明确的前后层级。

所以,我现在看长列表时,通常先问一个问题:这块材质需要跟着每一条数据重复出现吗?

没有明确必要时,我会优先把材质留给固定悬浮区域。列表本身的数据量较大时,再使用 LazyForEach 等按需创建方式处理大量 Item。

例如:

List() {
  LazyForEach(
    this.dataSource,
    (item: ItemData) => {
      ListItem() {
        this.itemBuilder(item)
      }
    },
    (item: ItemData) => item.id
  )
}

不过,这里需要把两个问题分开。LazyForEach 处理列表节点创建方式,材质到底需要出现在多少个 Item 上,仍然属于页面设计和性能决策。列表已经按需加载,并不意味着每一个可见 Item 都适合继续叠沉浸材质。

高频动画优先使用图形变换属性

动态页面中的另一个成本来自动画更新。例如悬浮工具栏跟随滚动退出、拖动面板移动、组件淡入淡出,这些状态可能在较短时间里连续变化。

如果高频过程中持续修改布局宽度、高度等属性,页面需要反复参与布局计算。HarmonyOS 当前 Code Linter 已经把使用图形变换替代部分布局属性动画列入推荐性能规则,并明确建议在动效丢帧场景下优先调整。

因此,高频状态可以优先围绕 translate 、opacity、scale 处理。

例如让一块悬浮工具栏退出页面:

this.getUIContext().animateTo({
  duration: 180,
  curve: Curve.EaseOut
}, () => {
  this.toolbarOpacity = 0;
  this.toolbarTranslateY = 80;
  this.toolbarScale = 0.96;
});

这些变化主要发生在视觉层面,适合滚动显隐这类高频场景。确实需要改变实际布局空间时,宽高变化仍然可以使用。这个时候更值得关注的是发生频率和组件复杂度。

我会简单分成两类:

频繁变化
→ translate / opacity / scale

低频结构变化
→ 根据页面需求调整布局

这种划分不追求统一写法,重点是避免一个高频手势每一帧都触发复杂布局调整。

动态状态单独更新

动态页面还容易出现另一个问题:材质对象跟着手势或者滚动不断创建。

例如下面的逻辑没有必要放进高频回调:

new uiMaterial.ImmersiveMaterial(...)

材质参数没有发生变化时,可以提前准备:

private readonly toolbarMaterial:
  uiMaterial.Material =
    new uiMaterial.ImmersiveMaterial({
      style: uiMaterial.ImmersiveStyle.THIN
    });

滚动和动画过程只更新 位置、透明度、缩放、显示状态 材质对象继续复用。

一定要减少频繁创建和销毁对象带来的开销。这里我们把稳定的材质配置和高频的动画状态分开,代码结构也会更容易维护。

性能结论仍然需要回到真机

页面结构已经整理以后,最后才进入真正的性能验证。

模拟器很适合确认 API、页面结构、状态流转和基础视觉结果,却无法替代目标设备上的 GPU、触控、功耗以及设备分档。

大家一定要区分模拟器、真机和预览器的调试差异,并记录了模拟器在图形显示等方面与真机可能存在差别。

因此,我们可以把验证范围分成两部分:

模拟器适合确认 真机继续确认
API 是否能够调用 实际帧率
页面结构是否正确 发热与功耗
状态切换是否执行 动画手感
材质是否进入基础显示 最终材质细节
HDS 查询是否返回结果 不同设备能力差异

等设备权限具备以后,长列表、动态面板和 HDS 材质都需要重新放到真机环境中测试。

三、降级策略最好在正式接入之前准备完成

属性冲突解决以后,页面结构也经过控制,某些设备或者某些业务场景仍然可能不适合继续保留完整材质。

这种情况很正常。沉浸光感本身会根据设备能力和系统设置调整表现,项目仍然需要负责自己的页面可读性、性能和业务稳定性。

我现在更愿意提前准备三层回退方式:先交给系统自适应,然后缩减页面中的材质和动态效果,最后保留普通背景方案。

第一层先使用系统能够自适应的方案

普通 ArkUI 页面没有特殊要求时,可以从相对克制的材质开始:

private readonly normalMaterial:
  uiMaterial.Material =
    new uiMaterial.ImmersiveMaterial({
      style: uiMaterial.ImmersiveStyle.THIN,
      applyShadow: true
    });

页面先不要同时加入过重的 materialColor、普通背景模糊和多套动态反馈。

HDS 组件也可以优先保留自适应思路。当前 UI Design Kit 6.1.0(23)已经引入 HDS 材质类型等接口,其中包括 IMMERSIVE

在已有项目中,我会先让系统承担更多设备适配工作,等页面结构稳定以后,再决定是否有必要手动控制材质等级。

可以简单理解为:

没有特殊设备要求
↓
先采用系统自适应策略

这一步能省掉很多过早的设备分支。

第二层根据 HDS 查询结果收敛策略

确实需要根据设备做判断时,可以读取:

hdsMaterial.getSystemMaterialTypes()

当前诊断页会检查返回结果中是否包含 IMMERSIVE

如果已经确认当前环境具备对应材质能力,页面继续保留自适应策略;当前环境没有明确确认 IMMERSIVE 时,实验页会提示向更轻量的 SMOOTH 策略收敛。

这里需要把它理解成项目中的保守策略。页面没有必要为了某一张截图中的视觉差异,把框架组件长期固定在高成本等级上。标题栏和底部导航会持续存在,对流畅度和稳定性的要求通常比单次视觉效果更重要。

第三层保留普通背景回退

经过前两层处理以后,仍然可能出现一些页面不适合继续使用材质。长列表已经有明显性能压力,复杂背景始终影响前景阅读,或者正式版本需要覆盖更多设备,而当前真机数据还不够完整,这些情况下都可以回到普通背景。

例如:

Column() {
  Text('普通背景回退')
}
.width('88%')
.height(108)
.borderRadius(24)
.backgroundColor('#F2FFFFFF')

我会把这种方案视为正常的工程回退。业务页面首先需要保证文字清楚、操作可用、滚动稳定。沉浸材质用于增强空间层级,它应该建立在这些基本条件已经满足的基础上。

整个降级过程可以按照下面的顺序推进:

系统自适应
↓
减少重复材质节点
↓
减少不必要的动态反馈
↓
根据设备能力收敛 HDS 策略
↓
普通背景回退

每一次只处理当前最明显的问题。

页面材质节点过多,就先减少数量;动态反馈造成压力,就先收掉多余动画;设备能力没有确认,再降低材质要求;这些调整仍然无法保证稳定时,再回到普通背景。这样比重新准备一整套备用页面更容易维护。

实验页面和正式业务页面还要再做一次清理

诊断页面中会出现很多正式产品不需要的内容。

例如:

错误属性组合
MaterialState 状态
HDS 查询结果
降级建议
测试说明

这些内容在定位问题时非常有用,发布到正式业务页面以后却没有必要一直存在。

项目收尾时,可以再检查一遍:

实验内容 正式页面处理
冲突对照卡片 删除
调试状态文字 删除或转入日志
HDS 查询 按设备策略决定是否保留
普通背景回退 保留
材质参数 保留业务需要部分
真机性能结论 进入发布前检查
实验按钮 删除

我比较在意这一轮清理。诊断代码长期留在业务组件中,后续维护时很容易分不清某个状态属于正式逻辑还是测试逻辑。实验完成以后及时收掉,页面会清爽很多。

总结

沉浸光感进入已有项目以后,比继续增加参数更重要的,是形成一个稳定的排查顺序。

页面出现异常时,我现在通常先从普通视觉属性开始检查,再进入材质参数和前景颜色。单个组件确认稳定以后,才把问题扩大到长列表、动画和设备能力。

整个过程可以记成:

视觉属性
↓
材质参数
↓
前景颜色
↓
页面结构
↓
设备能力
↓
降级策略

这个顺序能够减少很多无效调整。卡片已经受到不透明背景影响时,继续比较材质档位没有意义;列表本身存在大量节点和高频布局更新时,先去怀疑设备材质等级也很容易偏离真正的问题。

进入性能阶段以后,我更关注材质使用范围和动画更新方式。长列表通过 LazyForEach 等方式控制节点创建,高频变化尽量使用 translateopacityscale,材质对象则提前创建并保持复用。设备表现仍然不理想时,降级可以继续保持简单。先使用系统自适应,再减少材质节点和动态反馈,随后根据设备能力收敛 HDS 策略,最后保留普通背景回退。

我更看重页面最后是不是稳定。文字能够一直看清,操作响应自然,滚动保持流畅以后,沉浸光感才真正给页面增加了价值。某个页面为了保留材质效果而牺牲这些基本体验时,回退到更轻的方案会更加合适。

当前诊断页面可以在 HarmonyOS 7 模拟器中检查属性冲突、应用状态和 HDS 能力查询。正式用于项目之前,大家仍然需要在具备权限的真机上确认帧率、发热、动画手感以及不同设备能力下的最终材质表现。

完整代码

Main.ets

/**
 * HarmonyOS 7 沉浸光感深度实战 09
 *
 * 验证环境:
 * HarmonyOS SDK API 26
 * HarmonyOS 7 模拟器
 */

import { uiMaterial } from '@kit.ArkUI';
import { hdsMaterial } from '@kit.UIDesignKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Main {
  @State private materialStateText: string =
    '尚未读取';

  @State private materialStateColor:
    ResourceColor = '#68708A';

  @State private hdsTypesText: string =
    '尚未查询';

  @State private hdsStrategyText: string =
    '等待设备能力查询';

  @State private supportsImmersive: boolean =
    false;

  /**
   * 推荐组合:
   * 使用 THIN 和轻量透明赋色。
   * 组件自身不再设置普通背景、模糊和阴影。
   */
  private readonly safeMaterial:
    uiMaterial.Material =
      new uiMaterial.ImmersiveMaterial({
        style:
          uiMaterial.ImmersiveStyle.THIN,

        materialColor:
          '#33FFFFFF',

        applyShadow:
          true
      });

  /**
   * 对照组合:
   * materialColor 使用完全不透明的颜色,
   * 用于观察背景信息被明显压低后的表现。
   */
  private readonly opaqueMaterial:
    uiMaterial.Material =
      new uiMaterial.ImmersiveMaterial({
        style:
          uiMaterial.ImmersiveStyle.THIN,

        materialColor:
          '#FFFFFFFF',

        applyShadow:
          true
      });

  aboutToAppear(): void {
    this.loadArkUiState();
    this.loadHdsCapability();
  }

  /**
   * 读取应用级材质状态。
   */
  private loadArkUiState(): void {
    const info:
      uiMaterial.MaterialInfo =
        uiMaterial.getMaterialInfo();

    switch (info.state) {
      case uiMaterial.MaterialState.DEFAULT:
        this.materialStateText =
          'DEFAULT';

        this.materialStateColor =
          '#5065E8';
        break;

      case uiMaterial.MaterialState.ENABLE:
        this.materialStateText =
          'ENABLE';

        this.materialStateColor =
          '#1A8F5D';
        break;

      case uiMaterial.MaterialState.DISABLE:
        this.materialStateText =
          'DISABLE';

        this.materialStateColor =
          '#C85A3A';
        break;

      default:
        this.materialStateText =
          `UNKNOWN(${info.state})`;

        this.materialStateColor =
          '#68708A';
        break;
    }
  }

  /**
   * 查询 HDS 材质类型,
   * 同时生成当前页面使用的降级提示。
   */
  private loadHdsCapability(): void {
    try {
      const types:
        Array<hdsMaterial.MaterialType> =
          hdsMaterial
            .getSystemMaterialTypes();

      this.supportsImmersive =
        types.indexOf(
          hdsMaterial
            .MaterialType
            .IMMERSIVE
        ) >= 0;

      this.hdsTypesText =
        this.getHdsTypeText(types);

      this.hdsStrategyText =
        this.supportsImmersive
          ? '保持 ADAPTIVE'
          : '收敛到 SMOOTH';
    } catch (error) {
      const businessError =
        error as BusinessError;

      this.hdsTypesText =
        `查询失败 ${businessError.code} `
          + `${businessError.message}`;

      this.supportsImmersive =
        false;

      this.hdsStrategyText =
        '保留普通背景回退';
    }
  }

  /**
   * 把 HDS MaterialType 转换成页面文本。
   */
  private getHdsTypeText(
    types:
      Array<hdsMaterial.MaterialType>
  ): string {
    if (types.length === 0) {
      return '当前环境未返回材质类型';
    }

    const names: Array<string> = [];

    for (
      let index: number = 0;
      index < types.length;
      index++
    ) {
      const type:
        hdsMaterial.MaterialType =
          types[index];

      switch (type) {
        case hdsMaterial.MaterialType.NONE:
          names.push('NONE');
          break;

        case hdsMaterial
          .MaterialType
          .ADAPTIVE:
          names.push('ADAPTIVE');
          break;

        case hdsMaterial
          .MaterialType
          .IMMERSIVE:
          names.push('IMMERSIVE');
          break;

        default:
          names.push(
            `UNKNOWN(${type})`
          );
          break;
      }
    }

    return names.join('、');
  }

  @Builder
  private sectionTitle(
    title: string,
    description: string
  ) {
    Column({ space: 4 }) {
      Text(title)
        .fontSize(21)
        .fontWeight(FontWeight.Bold)
        .fontColor('#11182C')
        .width('100%')

      Text(description)
        .fontSize(13)
        .fontColor('#68708A')
        .lineHeight(20)
        .width('100%')
    }
    .width('100%')
    .alignItems(
      HorizontalAlign.Start
    )
  }

  /**
   * 当前环境查询区域。
   */
  @Builder
  private environmentPanel() {
    Column({ space: 10 }) {
      Row({ space: 12 }) {
        Text('MaterialState')
          .width('38%')
          .fontSize(13)
          .fontColor('#68708A')

        Text(this.materialStateText)
          .layoutWeight(1)
          .fontSize(13)
          .fontWeight(
            FontWeight.Medium
          )
          .fontColor(
            this.materialStateColor
          )
          .textAlign(TextAlign.End)
      }
      .width('100%')

      Divider()
        .color('#E8EBF2')

      Row({ space: 12 }) {
        Text('HDS MaterialType')
          .width('38%')
          .fontSize(13)
          .fontColor('#68708A')

        Text(this.hdsTypesText)
          .layoutWeight(1)
          .fontSize(13)
          .fontWeight(
            FontWeight.Medium
          )
          .fontColor('#17203A')
          .textAlign(TextAlign.End)
          .maxLines(3)
      }
      .width('100%')

      Divider()
        .color('#E8EBF2')

      Row({ space: 12 }) {
        Text('HDS 策略')
          .width('38%')
          .fontSize(13)
          .fontColor('#68708A')

        Text(this.hdsStrategyText)
          .layoutWeight(1)
          .fontSize(13)
          .fontWeight(
            FontWeight.Medium
          )
          .fontColor(
            this.supportsImmersive
              ? '#1A8F5D'
              : '#D06C35'
          )
          .textAlign(TextAlign.End)
          .maxLines(2)
      }
      .width('100%')
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(20)
  }

  /**
   * 三张对照卡片共用同一块背景。
   */
  @Builder
  private comparisonBackground() {
    Row() {
      Column()
        .width('34%')
        .height('100%')
        .backgroundColor('#4B62FF')

      Column()
        .width('32%')
        .height('100%')
        .backgroundColor('#59B7FF')

      Column()
        .layoutWeight(1)
        .height('100%')
        .backgroundColor('#A266FF')
    }
    .width('100%')
    .height('100%')
  }

  /**
   * 推荐组合。
   * 尺寸和圆角设置完成后,再设置系统材质。
   */
  @Builder
  private safeCard() {
    Stack() {
      this.comparisonBackground()

      Column({ space: 6 }) {
        Text('推荐组合')
          .fontSize(18)
          .fontWeight(
            FontWeight.Bold
          )
          .fontColor('#17203A')

        Text(
          '透明背景 · 轻量赋色 · systemMaterial 最后'
        )
          .fontSize(12)
          .fontColor('#596179')
          .textAlign(TextAlign.Center)
          .maxLines(2)
      }
      .width('88%')
      .height(108)
      .padding({
        left: 10,
        right: 10
      })
      .borderRadius(24)
      .justifyContent(
        FlexAlign.Center
      )
      .alignItems(
        HorizontalAlign.Center
      )
      .systemMaterial(
        this.safeMaterial
      )
    }
    .width('100%')
    .height(160)
    .borderRadius(24)
    .clip(true)
  }

  /**
   * 属性顺序对照。
   *
   * 这里故意把不透明背景色放在
   * systemMaterial 后面,
   * 用于诊断页面观察属性关系。
   */
  @Builder
  private wrongOrderCard() {
    Stack() {
      this.comparisonBackground()

      Column({ space: 6 }) {
        Text('属性顺序冲突')
          .fontSize(18)
          .fontWeight(
            FontWeight.Bold
          )
          .fontColor('#17203A')

        Text(
          'systemMaterial 后继续设置不透明背景'
        )
          .fontSize(12)
          .fontColor('#596179')
          .textAlign(TextAlign.Center)
          .maxLines(2)
      }
      .width('88%')
      .height(108)
      .padding({
        left: 10,
        right: 10
      })
      .borderRadius(24)
      .justifyContent(
        FlexAlign.Center
      )
      .alignItems(
        HorizontalAlign.Center
      )
      .systemMaterial(
        this.safeMaterial
      )
      .backgroundColor(
        Color.White
      )
    }
    .width('100%')
    .height(160)
    .borderRadius(24)
    .clip(true)
  }

  /**
   * 完全不透明 materialColor 对照。
   */
  @Builder
  private opaqueTintCard() {
    Stack() {
      this.comparisonBackground()

      Column({ space: 6 }) {
        Text('不透明 materialColor')
          .fontSize(18)
          .fontWeight(
            FontWeight.Bold
          )
          .fontColor('#17203A')

        Text(
          'materialColor = #FFFFFFFF'
        )
          .fontSize(12)
          .fontColor('#596179')
          .textAlign(TextAlign.Center)
      }
      .width('88%')
      .height(108)
      .borderRadius(24)
      .justifyContent(
        FlexAlign.Center
      )
      .alignItems(
        HorizontalAlign.Center
      )
      .systemMaterial(
        this.opaqueMaterial
      )
    }
    .width('100%')
    .height(160)
    .borderRadius(24)
    .clip(true)
  }

  /**
   * 页面底部保留简短排查顺序。
   */
  @Builder
  private diagnosticOrder() {
    Column({ space: 10 }) {
      Text('排查顺序')
        .fontSize(16)
        .fontWeight(
          FontWeight.Bold
        )
        .fontColor('#17203A')
        .width('100%')

      Text(
        '背景属性 → 材质参数 → 前景颜色 '
          + '→ 页面结构 → 设备能力'
      )
        .fontSize(13)
        .fontColor('#68708A')
        .lineHeight(20)
        .width('100%')

      Text(
        '出现性能压力时,先减少重复材质和动态效果,'
          + '再决定 HDS 等级或普通背景回退。'
      )
        .fontSize(12)
        .fontColor('#747C92')
        .lineHeight(19)
        .width('100%')
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(20)
  }

  build() {
    Scroll() {
      Column({ space: 18 }) {
        Column({ space: 6 }) {
          Text(
            'HarmonyOS 7 沉浸光感'
          )
            .fontSize(28)
            .fontWeight(
              FontWeight.Bold
            )
            .fontColor('#11182C')
            .width('100%')

          Text(
            '冲突诊断与降级策略'
          )
            .fontSize(16)
            .fontColor('#68708A')
            .width('100%')
        }
        .alignItems(
          HorizontalAlign.Start
        )
        .width('100%')

        this.sectionTitle(
          '当前环境',
          '先确认应用状态和 HDS 材质能力。'
        )

        this.environmentPanel()

        this.sectionTitle(
          '推荐组合',
          '组件只保留必要样式,再设置系统材质。'
        )

        this.safeCard()

        this.sectionTitle(
          '冲突示例',
          '下面两张卡片保留容易影响材质表现的配置。'
        )

        this.wrongOrderCard()

        this.opaqueTintCard()

        this.diagnosticOrder()

        Text(
          '模拟器用于排查属性和接口,'
            + '性能、发热与设备材质差异仍需真机验证。'
        )
          .fontSize(12)
          .fontColor('#747C92')
          .lineHeight(19)
          .padding({
            top: 4,
            bottom: 24
          })
          .width('100%')
      }
      .width('100%')
      .padding({
        left: 20,
        right: 20,
        top: 24,
        bottom: 24
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F4F6FB')
  }
}
Logo

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

更多推荐