【HarmonyOS 7 沉浸光感深度实战】10 通用组件封装与现有项目接入 原创
头像 小雨同学 2026-09-08 09:49:50    发布
0 浏览 0 点赞 0 收藏

前言

沉浸光感放进一个新页面时,接入过程通常比较轻。页面结构简单,组件数量有限,需要材质的区域创建 ImmersiveMaterial,随后通过 systemMaterial 应用到组件,基本就能完成第一轮实验。

已有项目里的情况会复杂许多。搜索框可能已经使用了半年,底部工具栏有自己的背景和阴影,Menu 分散在几个页面中,Sheet 也有现成的主题配置。这个阶段继续在每一个页面分别创建 ImmersiveMaterial,开始时改动不大,随着接入范围扩大,材质配置也会逐渐分散。

同样是搜索区域,有的页面使用 THIN,有的页面改成了 REGULAR;部分组件增加了 colorInvert,另外一些位置又单独设置了 materialColor;设备能力判断、普通背景回退和 HDS 配置也可能慢慢进入各个业务页面。

代码能够运行,后续维护却越来越难。我现在处理这类改造时,会先把材质相关的判断收拢起来。页面继续决定哪些位置需要沉浸光感,业务组件继续负责自己的内容和交互,材质样式、应用状态、HDS 能力查询以及普通背景回退,则交给单独的策略层处理。

普通 ArkUI 容器再增加一层比较轻的 ImmersiveSurface。搜索框、悬浮工具栏这类区域,只需要告诉 Surface 当前属于什么场景,具体使用哪一种材质、材质关闭以后用什么背景,都可以留在统一策略里面。

这样改造以后,后续调整会轻松不少。搜索区域需要更换材质样式,只需要修改策略;某些环境不适合继续使用材质,Surface 可以回到普通背景;业务页面原来的搜索、按钮、状态和事件都不用重新组织。

目前我的测试设备还没有 HarmonyOS 7 实机测试权限,因此这套封装先在模拟器中检查接口、组件结构、回退分支和页面调用。材质细节、自动反色、HDS 能力以及不同设备上的最终表现,仍然需要在具备权限的真机上重新确认。

一、已有页面先梳理接入位置

面对已经运行一段时间的项目,我通常不会先创建通用组件。我更习惯把现有页面过一遍,看看材质真正会出现在哪些地方。

原因在于,沉浸光感并不存在完全统一的接入入口。普通 ArkUI 容器、Popup、Menu、Sheet 和 HDS 框架组件都有自己的配置方式。为了追求表面上的统一,把这些入口全部塞进一个组件,最终得到的往往是一层职责越来越多的封装。

所以,第一步更适合先把页面分开。


现有位置接入方式封装方式
搜索框、普通卡片ImmersiveMaterial + systemMaterial适合使用 ImmersiveSurface
悬浮工具栏、操作区域ImmersiveMaterial + systemMaterial适合使用 ImmersiveSurface
Popup、Menu、Sheet各自配置中的 systemMaterial复用 MaterialPolicy
HdsNavigation、HdsTabs、MiniBarsystemMaterialEffect保留 HDS 自己的材质配置
普通内容区域普通背景延续原来的页面结构

这样划分以后,ImmersiveSurface 的职责就比较清楚了。它只负责普通 ArkUI 容器。

Menu 和 Sheet 已经有自己的材质入口,只需要从策略层取得材质对象;HDS 继续使用自己的 MaterialTypeMaterialLevel。策略层可以提供能力查询和判断依据,但不会把几套组件体系重新包成一种接口。

先确认工程是否已经满足 API 条件

现有项目开始改页面之前,我会先检查工程环境。我们目前围绕 API 26 的沉浸光感能力展开,因此至少需要确认:

HarmonyOS SDK API 26↓targetAPIVersion >= 26.0.0↓entry module 中配置 UIMaterial.state

工程本身还没有进入对应 API 版本时,先完成工程升级和基础验证会更加稳妥。否则页面已经改了很多,后面发现目标 API、Module 配置或者材质状态没有准备好,排查范围会明显扩大。这一步完成以后,再开始决定业务场景和材质之间的对应关系。

样式留在策略层

搜索框、工具栏、Menu 和 Sheet 可以先定义成几个稳定场景:

export enum MaterialScene {  SEARCH = 0,  TOOLBAR = 1,  MENU = 2,  SHEET = 3}

策略层内部再维护当前项目采用的映射:

SEARCH→ THIN​TOOLBAR→ THIN​MENU→ THICK​SHEET→ ULTRA_THICK

这组映射属于示例中的项目选择,不需要理解成 API 固定要求。它的作用主要是把技术参数从业务页面里收回来。

搜索页面只需要知道当前位置属于 SEARCH,工具栏只需要知道自己属于 TOOLBAR。以后页面设计发生变化,需要调整某个场景的材质样式,修改策略层就可以覆盖所有使用位置。

业务页面里也不会到处出现:

ImmersiveStyle.THINImmersiveStyle.THICKImmersiveStyle.ULTRA_THICK

阅读页面代码时,注意力仍然可以留在业务本身。

MaterialState 也交给策略层判断

材质能否正常使用,还要结合当前应用状态。

这一类判断同样没有必要散落到多个页面里。

策略层可以统一提供:

static canUseArkUiMaterial(): boolean {  const info: uiMaterial.MaterialInfo =    uiMaterial.getMaterialInfo();​  switch (info.state) {    case uiMaterial.MaterialState.DEFAULT:    case uiMaterial.MaterialState.ENABLE:      return true;​    case uiMaterial.MaterialState.DISABLE:      return false;​    default:      return false;  }}

页面最终只关心一个布尔状态:

允许使用沉浸材质或使用普通背景

DEFAULTENABLEDISABLE 的具体判断继续留在策略层。

这样处理以后,普通背景回退也有了统一的入口。后面应用切换到不使用材质的状态,业务页面不需要分别处理每一张卡片。

HDS 能力可以集中查询,配置入口继续保持独立

已有项目里还可能同时存在 HDS 页面。

这时候 MaterialPolicy 可以顺便集中查询:

hdsMaterial.getSystemMaterialTypes()

把当前环境返回的 HDS 材质类型整理出来。

不过,HDS 的实际材质配置仍然保留在自己的组件体系中。

HdsNavigationHdsTabs 和 MiniBar 继续使用 MaterialTypeMaterialLevelsystemMaterialEffect。普通 ArkUI 的 ImmersiveSurface 不参与这些配置。

整个关系可以整理成:

业务页面↓普通 ArkUI 区域→ ImmersiveSurface→ MaterialPolicy→ ImmersiveMaterial​Menu / Sheet→ MaterialPolicy→ 各自 systemMaterial​HDS 页面→ HDS MaterialType / MaterialLevel​材质不可用→ 普通背景

我比较喜欢这样的分层,因为每一套组件仍然按照自己的方式工作,公共部分只处理真正重复的材质策略。

策略边界确定以后,下面再处理真正需要复用的普通 ArkUI 容器。

二、ImmersiveSurface 保持轻量

我对这类基础组件的要求比较简单:解决外围材质问题就够了。ImmersiveSurface 不需要理解搜索逻辑,不需要处理按钮事件,也没有必要接管 Menu、Sheet 或页面数据。

它只负责几项外层配置:

scenematerialEnabledcolorInvertradiussurfaceHeightfallbackColorcontent

这里比最初版本增加了 surfaceHeight。原因来自 ArkTS 组件调用方式。Surface 使用尾随闭包接收业务内容以后,不再在调用结束后继续链式设置 .width().height(),而是把容器高度作为明确参数传入。这样调用关系会更加稳定。

业务内容通过 BuilderParam 传入

ImmersiveSurface 自己不准备默认业务内容。调用页面使用尾随闭包把自己的 Builder 传进来:

@BuilderParamcontent: () => void;

Surface 只负责在外面包一层材质或普通背景。

因此,Search 仍然属于页面:

@Builderprivate searchContent() {  Search({    value: '',    placeholder: '搜索项目中的内容'  })    .width('100%')    .height(48)    .backgroundColor(Color.Transparent)}

工具栏按钮也继续留在页面自己的 Builder 中。通用容器如果开始知道搜索关键词、收藏状态、分享事件或者业务数据,它很快就会从材质组件变成业务组件,后续复用范围反而越来越窄。

高度通过 Surface 参数明确传入

修正后的 Surface 增加:

surfaceHeight: number = 64;

搜索区域使用:

ImmersiveSurface({  scene: MaterialScene.SEARCH,  materialEnabled: this.materialEnabled,  radius: 24,  surfaceHeight: 64,  fallbackColor:    MaterialPolicy.getFallbackColor(      MaterialScene.SEARCH    )}) {  this.searchContent()}

工具栏则传入另一组高度:

ImmersiveSurface({  scene: MaterialScene.TOOLBAR,  materialEnabled: this.materialEnabled,  radius: 24,  surfaceHeight: 72,  fallbackColor:    MaterialPolicy.getFallbackColor(      MaterialScene.TOOLBAR    )}) {  this.toolbarContent()}

这样调整以后,Surface 仍然没有参与业务布局设计。页面决定当前区域需要多高,再把这个结果传进去。宽度继续由 Surface 占满当前父容器,页面外围的边距、最大宽度和多设备断点仍然由业务页面控制。

材质可用时保持透明背景

当前应用允许使用沉浸材质时,Surface 内部只保留必要的容器属性:

Column() {  this.content()}.width('100%').height(this.surfaceHeight).backgroundColor(Color.Transparent).borderRadius(this.radius).clip(true).systemMaterial(  MaterialPolicy.getArkUiMaterial(    this.scene,    this.colorInvert  ))

业务内容继续显示在容器内部,外围材质由 MaterialPolicy 根据场景提供。这里也继续保留一个习惯:尺寸、背景和圆角等普通属性先确定,systemMaterial 放在后面。

以后遇到视觉异常,属性关系会更容易检查。

材质关闭以后直接进入普通背景分支

materialEnabledfalse 时,Surface 使用普通背景:

Column() {  this.content()}.width('100%').height(this.surfaceHeight).backgroundColor(this.fallbackColor).borderRadius(this.radius).clip(true)

业务内容完全不需要变化。

搜索框仍然是原来的 Search,工具栏按钮仍然保留原来的事件,页面只是少了一层沉浸材质。

这也是我觉得通用 Surface 最有价值的地方。已有项目接入新视觉能力时,回退方案能够和正常方案共用同一套业务组件,后面处理兼容性和设备差异会轻松很多。

fallbackColor 最终应该回到项目主题资源

示例里的:

'#F2FFFFFF'

只是为了让 Demo 可以独立运行。

真正接入项目以后,我更建议页面传入自己的主题资源:

fallbackColor:  $r('app.color.search_surface_background')

随后在浅色和深色资源目录中分别维护同名资源。这样,材质与应用主题之间也不会混在一起。

可以把职责理解成:

沉浸材质可用→ systemMaterial沉浸材质关闭→ fallbackColor浅色 / 深色主题→ 应用颜色资源

材质负责材质,主题负责主题。这两层分开以后,后续关闭沉浸光感时,页面仍然能够自然回到项目原有的颜色体系。

colorInvert 继续由调用页面决定

Surface 会保留:

colorInvert: boolean = false;

默认状态仍然关闭。

原因来自实际业务内容。自动反色是否适合某个区域,与内部使用的 Text、Search、SymbolGlyph、Image 以及对应颜色资源都有关系。通用 Surface 只能知道外层是什么场景,无法替内部所有前景内容完成条件判断。

因此,真正需要复杂背景适配的搜索框可以明确传入:

colorInvert: true

普通工具栏没有这个需求时继续保持默认值。这种处理能让通用组件保持简单,也避免所有材质区域统一打开自动反色。

Menu 和 Sheet 复用策略层

普通 ArkUI 容器通过 Surface 统一以后,Menu 和 Sheet 依然保持自己的调用方式。

Menu 可以直接取得策略层材质:

Button('打开 Menu')  .bindMenu(    this.menuBuilder,    {      systemMaterial:        this.menuMaterial    }  )

这里的:

this.menuMaterial

在页面初始化时已经通过下面的方式取得。

MaterialPolicy.getArkUiMaterial(  MaterialScene.MENU)

Sheet 也是相同思路。它继续使用自己的 systemMaterial,普通背景回退则根据 materialEnabled 决定:

backgroundColor:  this.materialEnabled    ? Color.Transparent    : MaterialPolicy.getFallbackColor(        MaterialScene.SHEET      )

这时候三类组件虽然接入形式不同,材质策略仍然来自同一个地方。

  • 搜索框和工具栏使用 Surface。
  • Menu 和 Sheet 使用自己的材质入口。
  • HDS 继续使用 HDS 接口。

页面结构没有为了统一而统一,真正重复的策略却已经被收到了同一层。

Screenshot_2026-08-15T104955

Screenshot_2026-08-15T105015

组件结构稳定以后,真正进入已有项目时,还需要控制迁移节奏。

三、现有项目更适合分批迁移

通用组件写好以后,很容易产生一种冲动:既然已经封装完成,不如把项目里能找到的卡片全部替换掉。

我现在更愿意从几个高频位置开始。顶部搜索、底部操作栏、图片工具区域,这些组件面积相对有限,层级关系也比较明确。先让这些区域运行稳定,再决定是否继续处理 Menu、Sheet 和其他页面。这样做的好处是,出现问题以后更容易判断它来自策略、Surface 还是某一个业务页面。

工程环境先通过基础检查

真正开始迁移以前,可以先确认:


检查项目当前建议
HarmonyOS SDKAPI 26
targetAPIVersion不低于 26.0.0
Module 类型entry
UIMaterial metadata确认当前状态
测试环境区分模拟器与真机

这些条件属于整个项目的基础。

基础环境没有确认以前,不适合同时修改大量业务页面。

普通 ArkUI、系统浮层和 HDS 保持各自入口

迁移过程中,我会一直保留这三个方向:

普通容器→ ImmersiveSurfaceMenu / Sheet 等系统浮层→ 自己的 systemMaterialHDS Navigation / Tabs / MiniBar→ systemMaterialEffect

MaterialPolicy 可以被它们共同使用,但不会把三套能力硬塞进一个组件。这种边界能够让后面的升级更加容易。某套 API 发生变化时,修改范围也更加集中。

策略数量不要提前扩得太多

当前示例只有:

SEARCHTOOLBARMENUSHEET

四种场景。

对一个刚开始接入的项目来说,已经够用。

以后真的出现稳定的新场景,例如播放器控制区或者固定顶部操作区,再增加对应策略。没有必要刚开始就创建十几种预设,把每一种微小差异都变成一个枚举。

策略数量少,后面更容易回答一个很重要的问题:为什么这里使用这种材质?

自动反色继续由具体页面验证

Surface 已经支持 colorInvert,迁移时仍然不能统一打开。

真正使用之前,需要继续检查:

材质厚度↓前景组件属性↓颜色资源↓设备能力↓实际复杂背景

这些条件都和具体页面有关。通用策略负责提供能力,业务页面负责决定是否使用,这种分工会更加稳妥。

普通背景回退要真正跑一遍

我觉得这一项非常重要。页面接入完成以后,可以专门测试一次材质关闭状态。

调整应用级材质状态并重新构建安装以后,逐项检查:

  • 搜索框是否回到普通背景
  • 工具栏文字和按钮是否仍然清楚
  • Menu 是否还能正常打开
  • Sheet 是否仍然具有可读背景
  • 页面布局是否发生明显变化
  • 原来的业务事件是否继续执行

通用组件在材质开启时表现正常,只完成了一半。材质关闭以后,页面结构仍然稳定,业务操作仍然完整,回退策略才真正具有价值。

长列表不要因为有 Surface 就全部套一遍

通用组件存在以后,还有一个比较容易出现的问题:哪里都可以套,所以哪里都开始套。

长列表尤其需要克制。

例如:

顶部搜索→ ImmersiveSurface悬浮筛选→ ImmersiveSurface普通 ListItem→ 原有背景底部操作栏→ ImmersiveSurface

这种结构已经能够建立材质层级。

普通内容项继续保持原来的背景,可以减少重复材质节点,也能让悬浮区域更加容易辨认。列表自身的数据加载、组件复用和性能优化,则继续按照原来的 ArkUI 长列表方案处理。

多设备布局继续由业务页面控制

ImmersiveSurface 增加了 surfaceHeight,并不代表它应该开始负责响应式布局。

手机上的搜索区域可能占据整行,平板上可能限制最大宽度,折叠屏展开以后也可能重新安排页面结构。这些仍然属于业务页面。

Surface 只保留:

材质场景材质状态圆角高度回退背景业务内容

至于:

最大宽度左右边距断点单双栏横竖屏布局

继续放在页面这一层处理。我希望这个组件以后能够出现在不同页面里,所以不会把某一种设备布局规则提前写进它内部。

HDS 查询主要用于诊断和设备策略

MaterialPolicy 里保留 HDS 能力查询以后,页面可以显示当前环境返回的材质类型,也可以把结果用于测试记录。

不过,普通 ArkUI Surface 不会因为 HDS 查询结果改变自己的工作方式。

HDS 能力查询更多服务于:

确认当前测试环境记录设备结果排查 HDS 材质差异决定 HDS 是否需要更保守的等级

普通组件继续使用自己的 ArkUI 材质策略。这样能够避免两套体系互相牵连。

最后再回到真机和不同窗口形态

模拟器可以帮助我们确认:

MaterialPolicy 是否能够正常工作ImmersiveSurface 是否能够编译运行BuilderParam 是否能够正常传递业务内容MaterialState 回退分支是否正确Menu 和 Sheet 是否可以继续使用自己的材质

到了正式项目阶段,还需要在目标设备上继续检查:


验证内容主要观察内容
材质细节通透度、阴影、背景层次
自动反色复杂背景中的前景可读性
交互反馈按压和光感
长列表滚动稳定性
动态页面动画手感和帧率
HDS材质类型与等级
手机小窗口布局
折叠屏和平板宽窗口布局
回退状态普通背景是否完整可用

当前这套三文件结构只能完成模拟器阶段的第一轮验证。真机权限具备以后,还需要重新覆盖材质效果、设备差异和性能结果。

总结

已有项目接入沉浸光感以后,我越来越倾向于把通用封装控制得小一些。

MaterialPolicy 管材质场景、MaterialState、HDS 能力查询和普通背景策略。

ImmersiveSurface 管普通 ArkUI 容器的材质显示和回退。

业务页面继续管理 Search、工具栏、Menu、Sheet 以及原来的业务状态。

这样的结构不会为了沉浸光感重新组织整个项目。修正后的 ImmersiveSurface 还增加了 surfaceHeight,业务内容通过必传的 @BuilderParam 尾随闭包传入。这样既避开了 ArkTS 中不合适的链式组件调用,也让 Surface 的尺寸职责更加明确。

我也会继续控制材质策略的数量。

搜索框、工具栏、Menu 和 Sheet 已经覆盖了一批常见场景。后续出现真正稳定的新需求,再往策略层增加新的场景。材质对象保持提前创建和复用,避免进入滚动、动画和业务事件中反复生成。

自动反色、主题、多设备和 HDS 继续保留各自的边界。自动反色由具体页面判断,浅色和深色背景交给应用资源,多设备断点留在业务布局,HDS 继续使用自己的材质配置。

普通背景回退也应该一直保留。对已有项目来说,我觉得这层保障很重要。材质关闭以后,搜索、工具栏、Menu 和 Sheet 仍然能够正常工作,页面布局也没有受到影响,这套封装才真正适合进入长期维护。

当前示例可以先在 HarmonyOS 7 模拟器中检查策略、Surface、页面调用和回退分支。正式进入项目以后,仍然需要在具备权限的真机上复核材质、自动反色、HDS、长列表性能以及不同窗口形态下的实际页面表现。

完整代码

MaterialPolicy.ets

/** * HarmonyOS 7 沉浸光感通用策略 * * 负责: * 1. 普通 ArkUI 材质场景映射。 * 2. MaterialState 判断。 * 3. HDS 材质能力查询。 * 4. 普通背景回退颜色。 */import { uiMaterial } from '@kit.ArkUI';import { hdsMaterial } from '@kit.UIDesignKit';import { BusinessError } from '@kit.BasicServicesKit';export enum MaterialScene {  SEARCH = 0,  TOOLBAR = 1,  MENU = 2,  SHEET = 3}export interface HdsMaterialCapability {  typesText: string;  supportsImmersive: boolean;  queryFailed: boolean;}export class MaterialPolicy {  /**   * 固定材质对象提前创建。   * 页面滚动和动画期间直接复用。   */  private static readonly searchMaterial:    uiMaterial.Material =    new uiMaterial.ImmersiveMaterial({      style: uiMaterial.ImmersiveStyle.THIN    });  private static readonly searchInvertMaterial:    uiMaterial.Material =    new uiMaterial.ImmersiveMaterial({      style: uiMaterial.ImmersiveStyle.THIN,      colorInvert: true    });  private static readonly toolbarMaterial:    uiMaterial.Material =    new uiMaterial.ImmersiveMaterial({      style: uiMaterial.ImmersiveStyle.THIN    });  private static readonly toolbarInvertMaterial:    uiMaterial.Material =    new uiMaterial.ImmersiveMaterial({      style: uiMaterial.ImmersiveStyle.THIN,      colorInvert: true    });  private static readonly menuMaterial:    uiMaterial.Material =    new uiMaterial.ImmersiveMaterial({      style: uiMaterial.ImmersiveStyle.THICK    });  private static readonly sheetMaterial:    uiMaterial.Material =    new uiMaterial.ImmersiveMaterial({      style: uiMaterial.ImmersiveStyle.ULTRA_THICK    });  /**   * 判断当前应用状态是否允许普通组件主动使用材质。   */  static canUseArkUiMaterial(): boolean {    const info: uiMaterial.MaterialInfo =      uiMaterial.getMaterialInfo();    switch (info.state) {      case uiMaterial.MaterialState.DEFAULT:      case uiMaterial.MaterialState.ENABLE:        return true;      case uiMaterial.MaterialState.DISABLE:        return false;      default:        return false;    }  }  /**   * 页面显示当前 MaterialState。   */  static getArkUiStateText(): string {    const info: uiMaterial.MaterialInfo =      uiMaterial.getMaterialInfo();    switch (info.state) {      case uiMaterial.MaterialState.DEFAULT:        return 'DEFAULT';      case uiMaterial.MaterialState.ENABLE:        return 'ENABLE';      case uiMaterial.MaterialState.DISABLE:        return 'DISABLE';      default:        return `UNKNOWN(${info.state})`;    }  }  /**   * 根据业务场景返回已经创建好的材质对象。   *   * 静态方法中直接使用类名访问静态成员,   * 避免 standalone-this 检查错误。   */  static getArkUiMaterial(    scene: MaterialScene,    colorInvert: boolean = false  ): uiMaterial.Material {    switch (scene) {      case MaterialScene.SEARCH:        return colorInvert          ? MaterialPolicy.searchInvertMaterial          : MaterialPolicy.searchMaterial;      case MaterialScene.TOOLBAR:        return colorInvert          ? MaterialPolicy.toolbarInvertMaterial          : MaterialPolicy.toolbarMaterial;      case MaterialScene.MENU:        return MaterialPolicy.menuMaterial;      case MaterialScene.SHEET:        return MaterialPolicy.sheetMaterial;      default:        return MaterialPolicy.searchMaterial;    }  }  /**   * Demo 使用的普通背景回退颜色。   *   * 正式项目可以替换成 base / dark 主题资源。   */  static getFallbackColor(    scene: MaterialScene  ): ResourceColor {    switch (scene) {      case MaterialScene.SEARCH:        return '#F2FFFFFF';      case MaterialScene.TOOLBAR:        return '#EEFFFFFF';      case MaterialScene.MENU:        return '#F7FFFFFF';      case MaterialScene.SHEET:        return '#F7FFFFFF';      default:        return '#F2FFFFFF';    }  }  /**   * 查询当前环境支持的 HDS 材质类型。   */  static queryHdsCapability():    HdsMaterialCapability {    try {      const types:        Array<hdsMaterial.MaterialType> =        hdsMaterial.getSystemMaterialTypes();      if (types.length === 0) {        return {          typesText: '当前环境未返回 HDS 材质类型',          supportsImmersive: false,          queryFailed: false        };      }      const names: Array<string> = [];      let supportsImmersive: boolean = false;      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');            supportsImmersive = true;            break;          default:            names.push(`UNKNOWN(${type})`);            break;        }      }      return {        typesText: names.join('、'),        supportsImmersive: supportsImmersive,        queryFailed: false      };    } catch (error) {      const businessError =        error as BusinessError;      return {        typesText:        `查询失败 ${businessError.code} `          + `${businessError.message}`,        supportsImmersive: false,        queryFailed: true      };    }  }}

ImmersiveSurface.ets

/** * HarmonyOS 7 通用沉浸材质容器 * * 负责: * 1. 普通 ArkUI 区域的 systemMaterial 接入。 * 2. MaterialState 关闭后的普通背景回退。 * 3. 圆角和容器高度。 * 4. 外部业务内容插入。 */import {  MaterialPolicy,  MaterialScene} from './MaterialPolicy';@Componentexport struct ImmersiveSurface {  /**   * 当前业务场景。   */  scene: MaterialScene =    MaterialScene.SEARCH;  /**   * 当前是否允许显示沉浸材质。   */  materialEnabled: boolean = true;  /**   * 自动反色默认关闭。   */  colorInvert: boolean = false;  /**   * Surface 圆角。   */  radius: number = 24;  /**   * 高度由调用页面传入。   *   * Surface 自身始终占满父容器宽度,   * 避免在尾随闭包外继续调用 width / height。   */  surfaceHeight: number = 64;  /**   * 材质关闭以后使用的普通背景。   */  fallbackColor: ResourceColor =    '#F2FFFFFF';  /**   * 外部业务内容。   *   * 当前组件通过尾随闭包初始化,   * 因此不再设置引用 this 的默认 Builder。   */  @BuilderParam  content: () => void;  build() {    if (this.materialEnabled) {      Column() {        this.content()      }      .width('100%')      .height(this.surfaceHeight)      .backgroundColor(Color.Transparent)      .borderRadius(this.radius)      .clip(true)      .systemMaterial(        MaterialPolicy.getArkUiMaterial(          this.scene,          this.colorInvert        )      )    } else {      Column() {        this.content()      }      .width('100%')      .height(this.surfaceHeight)      .backgroundColor(this.fallbackColor)      .borderRadius(this.radius)      .clip(true)    }  }}

Main.ets

/** * HarmonyOS 7 沉浸光感深度实战 10 * * 验证环境: * HarmonyOS SDK API 26 * HarmonyOS 7 模拟器 */​import {  MaterialPolicy,  MaterialScene,  HdsMaterialCapability} from './MaterialPolicy';​import {  ImmersiveSurface} from './ImmersiveSurface';​@Entry@Componentstruct Main {  @State private materialEnabled:    boolean = false;​  @State private materialStateText:    string = '尚未读取';​  @State private hdsTypesText:    string = '尚未查询';​  @State private hdsSupportText:    string = '未确认';​  @State private showSheet:    boolean = false;​  @State private lastActionText:    string = '尚未执行操作';​  /**   * Menu 和 Sheet 自己已经提供 systemMaterial,   * 因此只从策略层取得对应材质。   */  private readonly menuMaterial =    MaterialPolicy.getArkUiMaterial(      MaterialScene.MENU    );​  private readonly sheetMaterial =    MaterialPolicy.getArkUiMaterial(      MaterialScene.SHEET    );​  aboutToAppear(): void {    this.loadMaterialEnvironment();  }​  /**   * 读取当前应用状态和 HDS 材质能力。   */  private loadMaterialEnvironment(): void {    this.materialEnabled =      MaterialPolicy.canUseArkUiMaterial();​    this.materialStateText =      MaterialPolicy.getArkUiStateText();​    const hdsCapability:      HdsMaterialCapability =      MaterialPolicy.queryHdsCapability();​    this.hdsTypesText =      hdsCapability.typesText;​    if (hdsCapability.queryFailed) {      this.hdsSupportText =        '查询失败';    } else if (      hdsCapability.supportsImmersive    ) {      this.hdsSupportText =        '支持 IMMERSIVE';    } else {      this.hdsSupportText =        '未确认 IMMERSIVE';    }  }​  @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('40%')          .fontSize(13)          .fontColor('#68708A')​        Text(this.materialStateText)          .layoutWeight(1)          .fontSize(13)          .fontWeight(FontWeight.Medium)          .fontColor(            this.materialEnabled              ? '#1A8F5D'              : '#D06C35'          )          .textAlign(TextAlign.End)      }      .width('100%')​      Divider()        .color('#E8EBF2')​      Row({ space: 12 }) {        Text('HDS MaterialType')          .width('40%')          .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('40%')          .fontSize(13)          .fontColor('#68708A')​        Text(this.hdsSupportText)          .layoutWeight(1)          .fontSize(13)          .fontWeight(FontWeight.Medium)          .fontColor('#5065E8')          .textAlign(TextAlign.End)          .maxLines(2)      }      .width('100%')    }    .width('100%')    .padding(16)    .backgroundColor(Color.White)    .borderRadius(20)  }​  /**   * 搜索框业务内容。   *   * Surface 负责外围材质,   * Search 自己继续负责搜索组件。   */  @Builder  private searchContent() {    Search({      value: '',      placeholder: '搜索项目中的内容'    })      .width('100%')      .height(48)      .backgroundColor(Color.Transparent)  }​  /**   * 工具栏业务内容。   */  @Builder  private toolbarContent() {    Row({ space: 10 }) {      Button('收藏')        .layoutWeight(1)        .height(38)        .fontSize(12)        .fontColor('#5065E8')        .backgroundColor('#EEF1FF')        .onClick(() => {          this.lastActionText =            '已执行收藏';        })​      Button('分享')        .layoutWeight(1)        .height(38)        .fontSize(12)        .fontColor('#5065E8')        .backgroundColor('#EEF1FF')        .onClick(() => {          this.lastActionText =            '已执行分享';        })​      Button('稍后')        .layoutWeight(1)        .height(38)        .fontSize(12)        .fontColor('#5065E8')        .backgroundColor('#EEF1FF')        .onClick(() => {          this.lastActionText =            '已加入稍后处理';        })    }    .width('100%')    .height('100%')    .padding({      left: 12,      right: 12    })    .alignItems(VerticalAlign.Center)  }​  /**   * Menu 内容。   */  @Builder  private menuBuilder() {    Menu() {      MenuItem({        content: '复制链接'      })        .onClick(() => {          this.lastActionText =            '已选择复制链接';        })​      MenuItem({        content: '加入收藏'      })        .onClick(() => {          this.lastActionText =            '已选择加入收藏';        })​      MenuItem({        content: '稍后处理'      })        .onClick(() => {          this.lastActionText =            '已选择稍后处理';        })    }  }​  /**   * Sheet 内容。   */  @Builder  private sheetBuilder() {    Column({ space: 16 }) {      Text('现有项目设置')        .fontSize(22)        .fontWeight(FontWeight.Bold)        .fontColor('#17203A')        .width('100%')​      Text(        'Sheet 继续保留自己的业务内容,'          + '材质统一从 MaterialPolicy 获取。'      )        .fontSize(14)        .fontColor('#68708A')        .lineHeight(21)        .width('100%')​      Column({ space: 12 }) {        Row({ space: 10 }) {          Text('接收更新提醒')            .layoutWeight(1)            .fontSize(14)            .fontColor('#17203A')​          Toggle({            type: ToggleType.Switch,            isOn: true          })        }        .width('100%')​        Divider()          .color('#E8EBF2')​        Row({ space: 10 }) {          Text('保留当前筛选')            .layoutWeight(1)            .fontSize(14)            .fontColor('#17203A')​          Toggle({            type: ToggleType.Switch,            isOn: false          })        }        .width('100%')      }      .width('100%')      .padding(16)      .backgroundColor('#66FFFFFF')      .borderRadius(18)​      Button('完成')        .width('100%')        .height(42)        .onClick(() => {          this.lastActionText =            'Sheet 操作完成';​          this.showSheet =            false;        })    }    .width('100%')    .height('100%')    .padding({      left: 24,      right: 24,      top: 18,      bottom: 24    })    .alignItems(HorizontalAlign.Start)  }​  /**   * 普通业务内容继续使用普通背景。   */  @Builder  private normalContentCard(    index: number  ) {    Column({ space: 6 }) {      Text(`普通内容区域 ${index}`)        .fontSize(17)        .fontWeight(FontWeight.Bold)        .fontColor('#17203A')        .width('100%')​      Text(        '普通内容继续使用原页面样式,'          + '无需全部改成沉浸材质。'      )        .fontSize(13)        .fontColor('#68708A')        .lineHeight(20)        .width('100%')    }    .width('100%')    .height(104)    .padding(16)    .justifyContent(FlexAlign.Center)    .alignItems(HorizontalAlign.Start)    .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(          '搜索区域',          'Search 保持原业务结构,Surface 负责外围材质。'        )​        ImmersiveSurface({          scene: MaterialScene.SEARCH,          materialEnabled: this.materialEnabled,          radius: 24,          surfaceHeight: 64,          fallbackColor:          MaterialPolicy.getFallbackColor(            MaterialScene.SEARCH          )        }) {          this.searchContent()        }​        this.sectionTitle(          '悬浮工具栏',          '按钮和业务事件继续保留在工具栏内部。'        )​        ImmersiveSurface({          scene: MaterialScene.TOOLBAR,          materialEnabled: this.materialEnabled,          radius: 24,          surfaceHeight: 72,          fallbackColor:          MaterialPolicy.getFallbackColor(            MaterialScene.TOOLBAR          )        }) {          this.toolbarContent()        }​        this.sectionTitle(          '普通内容',          '页面主体没有必要全部使用沉浸材质。'        )​        this.normalContentCard(1)        this.normalContentCard(2)​        this.sectionTitle(          '系统浮层',          'Menu 和 Sheet 继续使用各自的材质入口。'        )​        Row({ space: 12 }) {          Button('打开 Menu')            .layoutWeight(1)            .height(42)            .bindMenu(              this.menuBuilder,              {                systemMaterial:                this.menuMaterial              }            )​          Button(            this.showSheet              ? 'Sheet 已打开'              : '打开 Sheet'          )            .layoutWeight(1)            .height(42)            .onClick(() => {              this.showSheet = true;​              this.lastActionText =                'Sheet 已打开';            })            .bindSheet(              $this.showSheet,              this.sheetBuilder(),              {                height: 360,                dragBar: true,​                backgroundColor:                this.materialEnabled                  ? Color.Transparent                  : MaterialPolicy                  .getFallbackColor(                    MaterialScene.SHEET                  ),​                systemMaterial:                this.sheetMaterial              }            )        }        .width('100%')​        Column({ space: 6 }) {          Text('最近一次操作')            .fontSize(14)            .fontWeight(FontWeight.Medium)            .fontColor('#17203A')            .width('100%')​          Text(this.lastActionText)            .fontSize(13)            .fontColor('#68708A')            .lineHeight(20)            .width('100%')        }        .width('100%')        .padding(16)        .backgroundColor(Color.White)        .borderRadius(20)​        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')  }}


©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
HarmonyOS

暂无评论数据

加载中...

发布

地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除

京ICP备:2022009079号-2

京公网安备:11010502051901号

ICP证:京B2-20230255