前言

沉浸材质真正开始难调,通常出现在参数从一个变成一组之后。

一张卡片可以同时设置材质样式、材质赋色、自动反色、系统阴影、按压形变和触点光感。所有参数一起打开,页面看起来会更丰富,可一旦某个效果没有达到预期,大家很难马上判断问题出在颜色、阴影还是交互反馈上。

我第一次集中测试这些参数时,也经历过这种混乱。卡片颜色变了,文字对比度也跟着变化;按下卡片以后,形变和光感又同时出现。页面能够运行,却很难说清每个参数究竟做了什么。

后来我把它改成了一张可以反复操作的实验页面。卡片的位置、尺寸、圆角和背景始终不变,页面只通过开关替换材质参数。先看颜色、文字和阴影,再按住卡片观察形变,最后移动触点检查光感。每次只增加一个变量,页面上的变化也更容易解释。

当前实验固定使用 THIN 材质,并让卡片同时覆盖深色与浅色背景。卡片内部会显示当前参数状态,下方则提供五个开关和两个光感颜色选项。大家可以直接在页面中完成整轮实验,无需准备多组 Demo。

目前我的测试设备还没有 HarmonyOS 7 实机测试权限,因此当前页面先在模拟器中运行。模拟器适合核对接口、开关和基础显示;按压形变、触点光感及其响应节奏仍需要在具备权限的真机上复核。

一、六个参数可以沿着两条线理解

ImmersiveMaterial 的构造函数接收 ImmersiveOptions。当前可以配置 stylematerialColorcolorInvertapplyShadowinteractivelightEffect

没有传入参数时,系统会使用下面这组默认值:

{
  style: uiMaterial.ImmersiveStyle.REGULAR,
  materialColor: undefined,
  colorInvert: false,
  applyShadow: true,
  interactive: false,
  lightEffect: undefined
}

因此,一个没有额外配置的 ImmersiveMaterial 会使用 REGULAR 样式和系统材质阴影,赋色、自动反色、按压形变与触点光感则保持关闭。

六个参数放在一起时容易显得杂乱。按照它们在页面中的作用,可以先分成静态外观和动态反馈两组。

参数默认值页面中的作用实验时怎样观察
styleREGULAR决定材质的整体样式保持背景不变,切换样式
materialColorundefined改变材质整体色调来回切换赋色开关
colorInvertfalse调整子树中的文字和图标颜色让卡片横跨深浅背景
applyShadowtrue影响卡片与背景之间的层级观察圆角四周和下方
interactivefalse提供按压时的交互形变按下、保持,然后松开
lightEffectundefined提供触点附近的光感反馈按住卡片并缓慢移动触点

前四项主要改变组件停留在页面上的状态,后两项发生在操作过程中。实验顺序也可以顺着这两条线展开:先把颜色、文字和阴影看清楚,再加入按压形变与触点光感。

当前页面统一使用 THIN,主要是为了满足自动反色的条件。colorInvert 需要使用 THINULTRA_THIN,文字和图标还要通过 $r() 等资源接口设置颜色。直接写入 Color.White 或十六进制颜色时,自动反色不会按照资源颜色的路径生效。

把这些前提固定以后,页面中的主要变量就只剩五个开关和一个光感颜色。大家可以沿着同一套背景逐项操作,参数之间也不会互相遮挡。

二、用同一张卡片完成整轮参数实验

参数分组以后,页面结构还要尽量稳定。

每项参数使用一张独立卡片时,组件的位置、背景和尺寸也会随之变化。最后看到的差异可能同时来自材质参数和页面布局,很难继续判断。

当前页面只保留一张材质卡片。它横跨深蓝色和浅黄色两块背景,位置、尺寸和圆角始终不变。所有开关只负责重新创建卡片使用的 ImmersiveMaterial

先确认应用级状态

页面启动后先调用 getMaterialInfo(),读取当前应用的材质状态:

private loadMaterialState(): void {
  const info: uiMaterial.MaterialInfo =
    uiMaterial.getMaterialInfo();

  this.materialStateText =
    this.getMaterialStateName(info.state);

  this.stateColor =
    this.getMaterialStateColor(info.state);
}

页面读取到 DISABLE 时,应用中的沉浸式系统材质会被关闭。此时继续切换其他参数,也不会显示对应材质效果。DEFAULTENABLE 都允许普通组件主动设置材质。

所以,开始实验前可以先看页面顶部的 MaterialState。这个步骤很简单,却能避免把应用级配置问题误判成参数问题。

一张状态对象负责替换材质

页面通过 @State 保存当前材质:

@State private currentMaterial: uiMaterial.Material =
  new uiMaterial.ImmersiveMaterial({
    style: uiMaterial.ImmersiveStyle.THIN
  });

任意开关发生变化后,refreshMaterial() 都会按照当前参数重新创建一个 ImmersiveMaterial

关闭 materialColor 时,代码直接省略这个字段,让它保持 undefined。关闭 lightEffect 时,也会省略整个光感配置。这样可以保留接口的默认状态,无需使用临时颜色或空对象模拟关闭。

private refreshMaterial(): void {
  if (this.tintEnabled) {
    this.currentMaterial =
      new uiMaterial.ImmersiveMaterial({
        style: uiMaterial.ImmersiveStyle.THIN,
        materialColor: '#665B7CFF',
        colorInvert: this.colorInvertEnabled,
        applyShadow: this.applyShadowEnabled,
        interactive: this.interactiveEnabled
      });
    return;
  }

  this.currentMaterial =
    new uiMaterial.ImmersiveMaterial({
      style: uiMaterial.ImmersiveStyle.THIN,
      colorInvert: this.colorInvertEnabled,
      applyShadow: this.applyShadowEnabled,
      interactive: this.interactiveEnabled
    });
}

第一步先看基础材质

实验开始时,页面保持下面这组状态:

style = THIN
materialColor = undefined
colorInvert = false
applyShadow = true
interactive = false
lightEffect = undefined

这组配置相当于整轮实验的基准。大家可以先观察深浅背景透过卡片后的表现,再看圆角四周和卡片下方有没有系统阴影。

基础状态确认后,后面的每一步都只改变一个参数。

第二步打开 materialColor

materialColor 会在材质滤镜上继续混合一层颜色。它可以给搜索框、工具栏或操作卡片加入轻量的品牌色。

颜色需要保留透明度。完全不透明的颜色会遮住材质滤镜,组件容易变成一块普通色板。官方常见问题建议为 materialColor 传入带透明度的颜色。

当前实验使用下面的蓝紫色:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  materialColor: '#665B7CFF'
});

打开开关以后,可以观察卡片整体色调有没有变化,同时看左右两侧的背景还能保留多少信息。

背景仍然能够透出,说明材质还保留着原来的空间关系。颜色明显盖住背景文字和分区以后,可以在实际项目中继续降低 alpha。

赋色观察完成后,再关闭 materialColor。深浅背景恢复清楚,下一步查看文字变化会更容易。

第三步检查 colorInvert

colorInvert 用于让材质节点子树中的文字和图标根据背景调整颜色。搜索框、悬浮工具栏和跨越图片内容的浮动操作区,都可能用到这项能力。

自动反色需要同时满足几个条件:

  • 材质使用 THINULTRA_THIN
  • 文字与图标颜色通过资源接口设置。
  • 卡片下方存在明显的明暗变化。
  • 系统沉浸光感强度会影响反色触发阈值。

REGULARTHICKULTRA_THICK 不会进入相同的自动反色路径,硬编码颜色也不会触发相同的适配逻辑。

页面中的文字使用资源颜色:

Text('THIN 参数实验')
  .fontColor($r('app.color.material_demo_text'))

材质对象再打开自动反色:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  colorInvert: true
});

切换开关以后,可以直接观察标题和参数摘要在深色、浅色两侧的表现。

当前模拟器中没有出现明显变化时,大家可以依次检查材质样式、资源颜色和系统沉浸光感强度。上述条件都满足以后,仍然可以在具备权限的真机上继续复核。

第四步切换 applyShadow

applyShadow 默认开启,系统会为沉浸材质提供阴影。

组件已经设置 .shadow() 时,两套阴影可能叠加,卡片边缘容易显得偏重。项目已有统一阴影体系时,可以关闭系统材质阴影,继续使用原来的组件阴影。

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  applyShadow: false
});

当前实验没有增加自定义阴影。切换 applyShadow 时,只需要观察卡片下方、左右边缘和圆角区域。

阴影关闭以后,卡片与背景之间的距离感可能减弱。模拟器中的差异较小时,也可以直接回到基础状态来回切换,两种状态之间会更容易比较。

第五步单独打开 interactive

静态外观确认以后,可以保持 lightEffect 关闭,只打开 interactive

interactive 用于启用系统提供的交互形变。按下组件、保持片刻再松开,可以观察卡片在操作过程中的变化。

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  interactive: true
});

操作顺序可以保持简单:

按下卡片
↓
保持片刻
↓
松开

页面中的点击次数只能确认组件已经接收到操作。形变的幅度和恢复过程仍然要直接观察材质卡片。

第六步加入 lightEffect

lightEffect 用于配置触点附近的光感反馈。设置 lightEffect 后,color 可以指定光感颜色;color 未填写时,系统使用默认白色。

使用白色光感:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  interactive: true,
  lightEffect: {
    color: Color.White
  }
});

切换为蓝紫色:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  interactive: true,
  lightEffect: {
    color: '#FF77A0FF'
  }
});

打开光感以后,可以按住卡片并缓慢移动触点,然后再松开:

按下卡片
↓
缓慢移动触点
↓
观察光感位置和颜色
↓
松开并查看恢复

我更习惯先使用白色,确认触点附近有没有光感;随后切换蓝紫色,在相同位置重复操作。背景和操作方式保持一致以后,颜色配置是否生效会更容易辨认。

实验页面会在打开 lightEffect 时同步打开 interactive,关闭 interactive 时同步关闭光感。这个处理只用于减少无效的开关组合,两个参数在接口层面仍然保持独立。

三、把参数组合带回现有项目

实验页面解决了每个参数的作用,真正放进项目时,还需要结合组件的位置、内容和交互频率做取舍。

我更倾向于从一组简单配置开始。页面先稳定运行,再根据实际问题逐项增加参数。这样做虽然慢一点,却能避免颜色、阴影和交互同时变化后再回头排查。

轻量工具栏先处理文字适配

跨越复杂背景的顶部或底部工具栏,可以先使用:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  colorInvert: true,
  applyShadow: true
});

文字和图标需要通过资源接口设置颜色。静态效果稳定以后,再决定是否加入 interactive

工具栏操作较少时,按压反馈可以保持克制。工具栏中包含多个高频按钮时,再逐个判断哪些区域需要形变反馈。

带品牌色的操作卡片先控制透明度

材质需要带有轻量品牌色时,可以加入透明赋色:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  materialColor: '#665B7CFF',
  applyShadow: true,
  interactive: true
});

这类组件需要同时检查浅色模式、深色模式和图片背景。品牌色在纯色页面上看起来合适,换到复杂图片后可能显得偏重。

我通常会优先调整透明度,等颜色与背景达到平衡,再考虑增加光感。这样能够避免品牌色和触点光晕同时抢占注意力。

高频操作组件再加入光感

操作区域较大、交互频率较高时,可以同时加入形变和光感:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  interactive: true,
  lightEffect: {
    color: Color.White
  }
});

页面中已经存在较多动画时,光感颜色和使用范围可以适当收敛。多个高强度反馈集中出现,组件之间容易互相争夺注意力。

项目已有阴影时先明确归属

项目已经有自己的阴影规范时,可以关闭材质阴影:

new uiMaterial.ImmersiveMaterial({
  style: uiMaterial.ImmersiveStyle.THIN,
  applyShadow: false
});

随后继续使用原来的 .shadow()

阴影由系统材质负责,或者由组件样式负责,需要在接入时确定下来。同一个组件保留两套未经比较的阴影,页面层级很难保持统一。

整个调整过程可以沿着下面的路径推进:

确定 style
↓
决定是否加入 materialColor
↓
检查资源颜色与 colorInvert
↓
明确阴影来源
↓
最后加入 interactive 与 lightEffect

每一步只增加一个变量。页面出现问题时,也可以直接回到最近一次修改,不需要从整组参数中重新排查。

总结

把六个参数放进同一张实验页面以后,原本容易混在一起的变化会清楚很多。

materialColor 改变材质整体色调,透明度决定了颜色和背景之间的平衡。colorInvert 需要薄材质、资源颜色和明显的明暗背景。applyShadow 则要和项目原有的阴影体系明确分工。

按下卡片以后,interactive 负责交互形变,lightEffect 负责触点附近的光感。先观察纯形变,再加入光感和颜色,整个过程更容易理解。

我更推荐先把静态外观调稳定,再增加动态反馈。颜色、文字和阴影确认以后,按压形变与光感才有清楚的观察基础。

目前我的测试设备还没有 HarmonyOS 7 实机测试权限,因此当前页面先在模拟器中运行,可以确认接口调用、参数切换和基础表现。正式接入项目前,大家仍应优先在具备权限的真机上复核自动反色、按压形变、触点光感和性能表现。

完整代码

resources/base/element/color.json

{
  "color": [
    {
      "name": "material_demo_text",
      "value": "#FF17203A"
    },
    {
      "name": "material_demo_subtext",
      "value": "#FF596179"
    }
  ]
}

Main.ets

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

import { uiMaterial } from '@kit.ArkUI';

@Entry
@Component
struct Main {
  @State private materialStateText: string =
    '当前应用尚未读取状态';

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

  @State private tintEnabled: boolean = false;

  @State private colorInvertEnabled: boolean = false;

  @State private applyShadowEnabled: boolean = true;

  @State private interactiveEnabled: boolean = false;

  @State private lightEffectEnabled: boolean = false;

  /**
   * 0 表示白色光感。
   * 1 表示蓝紫色光感。
   */
  @State private lightColorMode: number = 0;

  @State private pressCount: number = 0;

  /**
   * 页面始终使用 THIN。
   * 参数变化后,refreshMaterial 会替换当前材质。
   */
  @State private currentMaterial: uiMaterial.Material =
    new uiMaterial.ImmersiveMaterial({
      style: uiMaterial.ImmersiveStyle.THIN
    });

  aboutToAppear(): void {
    this.loadMaterialState();
    this.refreshMaterial();
  }

  /**
   * 读取应用当前采用的材质状态。
   */
  private loadMaterialState(): void {
    const info: uiMaterial.MaterialInfo =
      uiMaterial.getMaterialInfo();

    this.materialStateText =
      this.getMaterialStateName(info.state);

    this.stateColor =
      this.getMaterialStateColor(info.state);
  }

  private getMaterialStateName(
    state: uiMaterial.MaterialState
  ): string {
    switch (state) {
      case uiMaterial.MaterialState.DEFAULT:
        return 'DEFAULT';

      case uiMaterial.MaterialState.ENABLE:
        return 'ENABLE';

      case uiMaterial.MaterialState.DISABLE:
        return 'DISABLE';

      default:
        return `未知状态 ${state}`;
    }
  }

  private getMaterialStateColor(
    state: uiMaterial.MaterialState
  ): ResourceColor {
    switch (state) {
      case uiMaterial.MaterialState.DEFAULT:
        return '#5065E8';

      case uiMaterial.MaterialState.ENABLE:
        return '#1A8F5D';

      case uiMaterial.MaterialState.DISABLE:
        return '#C85A3A';

      default:
        return '#68708A';
    }
  }

  /**
   * 按照当前开关状态重新创建材质对象。
   *
   * materialColor 和 lightEffect 关闭时直接省略字段,
   * 保持接口默认的 undefined 状态。
   */
  private refreshMaterial(): void {
    const lightColor: ResourceColor =
      this.lightColorMode === 0
        ? Color.White
        : '#FF77A0FF';

    if (this.tintEnabled) {
      if (this.lightEffectEnabled) {
        this.currentMaterial =
          new uiMaterial.ImmersiveMaterial({
            style: uiMaterial.ImmersiveStyle.THIN,
            materialColor: '#665B7CFF',
            colorInvert: this.colorInvertEnabled,
            applyShadow: this.applyShadowEnabled,
            interactive: this.interactiveEnabled,
            lightEffect: {
              color: lightColor
            }
          });
      } else {
        this.currentMaterial =
          new uiMaterial.ImmersiveMaterial({
            style: uiMaterial.ImmersiveStyle.THIN,
            materialColor: '#665B7CFF',
            colorInvert: this.colorInvertEnabled,
            applyShadow: this.applyShadowEnabled,
            interactive: this.interactiveEnabled
          });
      }

      return;
    }

    if (this.lightEffectEnabled) {
      this.currentMaterial =
        new uiMaterial.ImmersiveMaterial({
          style: uiMaterial.ImmersiveStyle.THIN,
          colorInvert: this.colorInvertEnabled,
          applyShadow: this.applyShadowEnabled,
          interactive: this.interactiveEnabled,
          lightEffect: {
            color: lightColor
          }
        });
    } else {
      this.currentMaterial =
        new uiMaterial.ImmersiveMaterial({
          style: uiMaterial.ImmersiveStyle.THIN,
          colorInvert: this.colorInvertEnabled,
          applyShadow: this.applyShadowEnabled,
          interactive: this.interactiveEnabled
        });
    }
  }

  /**
   * 将页面恢复到基础实验状态。
   */
  private resetExperiment(): void {
    this.tintEnabled = false;
    this.colorInvertEnabled = false;
    this.applyShadowEnabled = true;
    this.interactiveEnabled = false;
    this.lightEffectEnabled = false;
    this.lightColorMode = 0;
    this.pressCount = 0;
    this.refreshMaterial();
  }

  private getCurrentConfigText(): string {
    const tintText: string =
      this.tintEnabled ? '赋色开' : '赋色关';

    const invertText: string =
      this.colorInvertEnabled ? '反色开' : '反色关';

    const shadowText: string =
      this.applyShadowEnabled ? '阴影开' : '阴影关';

    const interactiveText: string =
      this.interactiveEnabled ? '形变开' : '形变关';

    const lightText: string =
      this.lightEffectEnabled ? '光感开' : '光感关';

    return `${tintText} · ${invertText} · ${shadowText} · `
      + `${interactiveText} · ${lightText}`;
  }

  private getOperationHint(): string {
    if (this.lightEffectEnabled) {
      return '按住卡片并缓慢移动触点,再松手观察恢复';
    }

    if (this.interactiveEnabled) {
      return '按住卡片片刻,再松手观察形变与恢复';
    }

    return '切换参数,观察颜色、文字和阴影变化';
  }

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

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

  /**
   * 深浅分区背景用于观察赋色和自动反色。
   */
  @Builder
  private comparisonBackground() {
    Stack() {
      Row() {
        Column({ space: 8 }) {
          Text('DARK')
            .fontSize(26)
            .fontWeight(FontWeight.Bold)
            .fontColor('#99FFFFFF')

          Text('深色背景')
            .fontSize(12)
            .fontColor('#BFFFFFFF')
        }
        .width('50%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
        .backgroundColor('#17203A')

        Column({ space: 8 }) {
          Text('LIGHT')
            .fontSize(26)
            .fontWeight(FontWeight.Bold)
            .fontColor('#6617203A')

          Text('浅色背景')
            .fontSize(12)
            .fontColor('#9917203A')
        }
        .layoutWeight(1)
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
        .backgroundColor('#F4C95D')
      }
      .width('100%')
      .height('100%')

      Row() {
        Circle()
          .width(58)
          .height(58)
          .fill('#4B62FF')

        Blank()

        Circle()
          .width(58)
          .height(58)
          .fill('#A266FF')
      }
      .width('100%')
      .padding({
        left: 24,
        right: 24
      })
    }
    .width('100%')
    .height('100%')
  }

  /**
   * 同一张卡片承载全部参数实验。
   *
   * 文字使用资源颜色,为 colorInvert 提供观察条件。
   */
  @Builder
  private materialPreview() {
    Stack() {
      this.comparisonBackground()

      Column({ space: 9 }) {
        Text('THIN 参数实验')
          .fontSize(21)
          .fontWeight(FontWeight.Bold)
          .fontColor(
            $r('app.color.material_demo_text')
          )
          .textAlign(TextAlign.Center)
          .width('100%')

        Text(this.getCurrentConfigText())
          .fontSize(12)
          .fontColor(
            $r('app.color.material_demo_subtext')
          )
          .textAlign(TextAlign.Center)
          .maxLines(2)
          .width('100%')

        Text(this.getOperationHint())
          .fontSize(12)
          .fontColor(
            $r('app.color.material_demo_subtext')
          )
          .textAlign(TextAlign.Center)
          .maxLines(2)
          .width('100%')
          .margin({ top: 4 })

        Text(`完成点击:${this.pressCount}`)
          .fontSize(11)
          .fontColor(
            $r('app.color.material_demo_subtext')
          )
      }
      .width('88%')
      .height(158)
      .padding({
        left: 12,
        right: 12
      })
      .backgroundColor(Color.Transparent)
      .borderRadius(30)
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center)
      .onClick(() => {
        this.pressCount += 1;
      })
      .systemMaterial(this.currentMaterial)
    }
    .width('100%')
    .height(228)
    .borderRadius(30)
    .clip(true)
  }

  @Builder
  private materialStateCard() {
    Row({ space: 12 }) {
      Text('MaterialState')
        .width('42%')
        .fontSize(14)
        .fontColor('#68708A')

      Text(this.materialStateText)
        .layoutWeight(1)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor(this.stateColor)
        .textAlign(TextAlign.End)
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(20)
  }

  @Builder
  private parameterPanel() {
    Column() {
      Row({ space: 12 }) {
        Column({ space: 3 }) {
          Text('materialColor')
            .fontSize(15)
            .fontWeight(FontWeight.Medium)
            .fontColor('#17203A')

          Text('叠加带透明度的蓝紫色')
            .fontSize(12)
            .fontColor('#747C92')
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Start)

        Toggle({
          type: ToggleType.Switch,
          isOn: this.tintEnabled
        })
          .onChange((isOn: boolean) => {
            this.tintEnabled = isOn;
            this.refreshMaterial();
          })
      }
      .width('100%')
      .padding({ top: 12, bottom: 12 })
      .alignItems(VerticalAlign.Center)

      Divider()
        .color('#E8EBF2')

      Row({ space: 12 }) {
        Column({ space: 3 }) {
          Text('colorInvert')
            .fontSize(15)
            .fontWeight(FontWeight.Medium)
            .fontColor('#17203A')

          Text('使用资源颜色观察自动反色')
            .fontSize(12)
            .fontColor('#747C92')
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Start)

        Toggle({
          type: ToggleType.Switch,
          isOn: this.colorInvertEnabled
        })
          .onChange((isOn: boolean) => {
            this.colorInvertEnabled = isOn;
            this.refreshMaterial();
          })
      }
      .width('100%')
      .padding({ top: 12, bottom: 12 })
      .alignItems(VerticalAlign.Center)

      Divider()
        .color('#E8EBF2')

      Row({ space: 12 }) {
        Column({ space: 3 }) {
          Text('applyShadow')
            .fontSize(15)
            .fontWeight(FontWeight.Medium)
            .fontColor('#17203A')

          Text('控制材质自带的系统阴影')
            .fontSize(12)
            .fontColor('#747C92')
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Start)

        Toggle({
          type: ToggleType.Switch,
          isOn: this.applyShadowEnabled
        })
          .onChange((isOn: boolean) => {
            this.applyShadowEnabled = isOn;
            this.refreshMaterial();
          })
      }
      .width('100%')
      .padding({ top: 12, bottom: 12 })
      .alignItems(VerticalAlign.Center)

      Divider()
        .color('#E8EBF2')

      Row({ space: 12 }) {
        Column({ space: 3 }) {
          Text('interactive')
            .fontSize(15)
            .fontWeight(FontWeight.Medium)
            .fontColor('#17203A')

          Text('控制按压时的交互形变')
            .fontSize(12)
            .fontColor('#747C92')
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Start)

        Toggle({
          type: ToggleType.Switch,
          isOn: this.interactiveEnabled
        })
          .onChange((isOn: boolean) => {
            this.interactiveEnabled = isOn;

            if (!isOn) {
              this.lightEffectEnabled = false;
            }

            this.refreshMaterial();
          })
      }
      .width('100%')
      .padding({ top: 12, bottom: 12 })
      .alignItems(VerticalAlign.Center)

      Divider()
        .color('#E8EBF2')

      Row({ space: 12 }) {
        Column({ space: 3 }) {
          Text('lightEffect')
            .fontSize(15)
            .fontWeight(FontWeight.Medium)
            .fontColor('#17203A')

          Text('控制触点附近的光感反馈')
            .fontSize(12)
            .fontColor('#747C92')
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Start)

        Toggle({
          type: ToggleType.Switch,
          isOn: this.lightEffectEnabled
        })
          .onChange((isOn: boolean) => {
            this.lightEffectEnabled = isOn;

            if (isOn) {
              this.interactiveEnabled = true;
            }

            this.refreshMaterial();
          })
      }
      .width('100%')
      .padding({ top: 12, bottom: 12 })
      .alignItems(VerticalAlign.Center)
    }
    .width('100%')
    .padding({
      left: 16,
      right: 16
    })
    .backgroundColor(Color.White)
    .borderRadius(20)
  }

  @Builder
  private lightColorPanel() {
    Column({ space: 10 }) {
      Text('光感颜色')
        .fontSize(15)
        .fontWeight(FontWeight.Medium)
        .fontColor('#17203A')
        .width('100%')

      Row({ space: 12 }) {
        Button('白色')
          .layoutWeight(1)
          .height(40)
          .fontSize(13)
          .fontColor(
            this.lightColorMode === 0
              ? Color.White
              : '#5065E8'
          )
          .backgroundColor(
            this.lightColorMode === 0
              ? '#5065E8'
              : '#EEF1FF'
          )
          .onClick(() => {
            this.lightColorMode = 0;
            this.refreshMaterial();
          })

        Button('蓝紫色')
          .layoutWeight(1)
          .height(40)
          .fontSize(13)
          .fontColor(
            this.lightColorMode === 1
              ? Color.White
              : '#5065E8'
          )
          .backgroundColor(
            this.lightColorMode === 1
              ? '#5065E8'
              : '#EEF1FF'
          )
          .onClick(() => {
            this.lightColorMode = 1;
            this.refreshMaterial();
          })
      }
      .width('100%')

      Text(
        '光感关闭时,颜色选择会保留,页面不会显示触点光感。'
      )
        .fontSize(12)
        .fontColor('#747C92')
        .lineHeight(19)
        .width('100%')

      Button('恢复默认参数')
        .width('100%')
        .height(42)
        .fontSize(14)
        .fontColor('#5065E8')
        .backgroundColor('#EEF1FF')
        .margin({ top: 4 })
        .onClick(() => {
          this.resetExperiment();
        })
    }
    .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('ImmersiveOptions 参数实验')
            .fontSize(16)
            .fontColor('#68708A')
            .width('100%')
        }
        .alignItems(HorizontalAlign.Start)
        .width('100%')

        this.sectionTitle(
          '当前应用状态',
          'DISABLE 状态会关闭页面中的沉浸式系统材质。'
        )

        this.materialStateCard()

        this.sectionTitle(
          '材质实验区',
          '切换参数后,直接观察卡片的颜色、文字、阴影和按压反馈。'
        )

        this.materialPreview()

        this.sectionTitle(
          '参数开关',
          '页面固定使用 THIN,每次只修改一项参数。'
        )

        this.parameterPanel()

        this.lightColorPanel()

        Text(
          '模拟器用于核对接口和基础表现,动态反馈仍需在真机上复核。'
        )
          .fontSize(13)
          .fontColor('#747C92')
          .lineHeight(20)
          .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、测试、元服务和应用上架分发等。

更多推荐