引言

在移动应用开发中,图片是最常用的视觉元素之一。无论是商品展示、用户头像、Banner轮播还是内容插图,都离不开 Image 组件的支持。HarmonyOS NEXT 的 Image 组件不仅支持本地资源和网络图片的加载显示,还提供了丰富的渲染属性——objectFit 填充模式、borderRadius 圆角裁剪、opacity 透明度、border 边框样式、blur 高斯模糊等,让开发者能够在组件层面完成图片的视觉美化,而无需借助原生图像处理库。

本文将通过一个完整的"图片美化工具"Demo,系统讲解 Image 组件的各项可调节属性。我们将构建一个实时预览工具,通过滑块和按钮动态调整图片的填充模式、圆角半径、透明度、边框宽度和模糊效果,让读者直观感受每个属性的作用和适用场景。

Image 组件概述

基本用法

Image 组件的使用非常直观,通过 src 参数指定图片来源:

// 加载本地资源
Image($r('app.media.app_icon'))

// 加载网络图片
Image('https://example.com/image.jpg')
  • 本地资源:使用 $r('app.media.xxx') 引用 resources/base/media/ 目录下的图片文件
  • 网络图片:直接传入 HTTPS URL,Image 组件会自动发起网络请求并展示图片

核心属性一览

属性 类型 说明
objectFit ImageFit 图片在容器内的填充模式
borderRadius Length 圆角半径,可实现圆形裁剪
opacity number 透明度,0.0 ~ 1.0
border BorderOptions 边框样式、宽度、颜色
blur number 高斯模糊半径
onError callback 图片加载失败回调
onComplete callback 图片加载成功回调
在这里插入图片描述

Demo:图片美化工具

我们的 Demo 构建了一个"图片美化工具",通过滑块实时调节各项属性,并在预览区即时展示效果。页面分为六个区域:

  1. 实时预览区:大尺寸图片预览,展示当前参数组合的效果
  2. ObjectFit 模式选择:6种填充模式的快速切换按钮
  3. 属性调节面板:圆角、透明度、边框、模糊四个滑条
  4. 图片来源切换:在本地资源和网络图片之间切换
  5. 模式对比一览:6种填充模式的对比网格
  6. 快捷操作:更换图片和重置参数按钮

状态变量设计

@State fitMode: number = 0;
@State cornerRadius: number = 0;
@State imageOpacity: number = 1.0;
@State imgBorderWidth: number = 0;
@State useNetwork: boolean = true;
@State blurRadius: number = 0;
@State currentUrl: string = SAMPLE_URL;

这里的 useNetwork 是一个布尔开关,控制预览区使用网络图片还是本地资源。currentUrl 则是网络图片的具体地址,通过"更换图片"按钮随机更新。

fillLabels 与 fillValues 的映射

为了方便在 UI 中展示和切换填充模式,我们使用了两个配对数组:

fitLabels: string[] = ['Cover', 'Contain', 'Fill', 'None', 'ScaleDown', 'Auto'];
fitValues: ImageFit[] = [ImageFit.Cover, ImageFit.Contain, ImageFit.Fill,
  ImageFit.None, ImageFit.ScaleDown, ImageFit.Auto];

fitLabels 用于 UI 展示(Grid 中的标签文字),fitValues 用于实际的属性赋值。通过同一个索引 fitMode 来关联两者:this.fitValues[this.fitMode] 获取当前选中的 ImageFit 枚举值,this.fitLabels[this.fitMode] 获取对应的显示文字。

实时预览区

预览区是整个页面最核心的交互区域:

Stack() {
  Row() {
    if (this.useNetwork) {
      Image(this.currentUrl)
        .width('100%')
        .height(200)
        .objectFit(this.fitValues[this.fitMode])
        .borderRadius(this.cornerRadius)
        .opacity(this.imageOpacity)
        .border({
          width: this.imgBorderWidth,
          color: '#1677FF',
          style: BorderStyle.Solid
        })
        .blur(this.blurRadius)
        .onError(() => {
          AlertDialog.show({ message: '图片加载失败,请检查网络' });
        })
    } else {
      Image($r('app.media.startIcon'))
        // ... 相同属性配置
    }
  }
  .width('100%')
  .height(200)
  .backgroundColor('#F5F6FA')

  Column() {
    Text(`Fit: ${this.fitLabels[this.fitMode]}`)
      .fontColor(Color.White)
      .backgroundColor('#00000060')
    Text(`Radius: ${this.cornerRadius}vp · Opacity: ${(this.imageOpacity * 100).toFixed(0)}%`)
      .fontColor('#FFFFFFCC')
      .backgroundColor('#00000060')
  }
  .position({ left: 12, bottom: 12 })
}

设计亮点:

  1. Stack 叠加层:预览图在下层,参数信息覆盖在左下角,使用半透明黑色背景保证文字可读性
  2. 所有属性同时生效:objectFit、borderRadius、opacity、border、blur 这几个属性可以组合使用,产生叠加效果
  3. onError 回调:网络图片可能加载失败(如网络不可达),通过 onError 回调弹出提示
  4. 背景色兜底:预览区的灰色背景 #F5F6FA 确保即使图片为空也有视觉占位
    在这里插入图片描述

六种 ObjectFit 模式详解

objectFit 决定了图片如何适配其容器。容器由 widthheight 定义了一个固定比例的区域(我们的预览区是 width:100% × height:200vp),但图片自身的宽高比可能与此不同。objectFit 就是处理这种不匹配的策略。

ImageFit.Cover(覆盖)

缩放图片以填满容器,保持原始宽高比。超出容器的部分会被裁剪。

适用场景:Banner图、背景图、卡片封面。这是最常用的模式,能保证容器被完整填满,不留空白。

ImageFit.Contain(容纳)

缩放图片以完整显示在容器内,保持原始宽高比。如果图片比例与容器不同,会出现留白区域。

适用场景:产品详情大图、需要看到图片全貌的场景。Contain 确保图片不会被裁切,但可能会在四周留出空白。

ImageFit.Fill(拉伸)

将图片拉伸以填满容器,不保持原始宽高比。图片可能会被压扁或拉长。

适用场景:较少使用,因为图片变形通常不符合设计预期。有时用于特殊视觉效果。

ImageFit.None(无缩放)

保持图片原始尺寸,不进行缩放。如果图片大于容器,超出部分被裁剪;如果图片小于容器,会有留白。

适用场景:图标、Logo 等固定尺寸的小图。

ImageFit.ScaleDown(缩小时容纳)

这是 None 和 Contain 的结合体。当图片大于容器时,行为同 Contain(等比缩小以完整显示);当图片小于容器时,行为同 None(保持原始尺寸不放大)。

适用场景:需要保证图片不被放大导致模糊,同时大图又不能撑破容器的场景。

ImageFit.Auto(自动)

由系统根据图片和容器特性自动选择最合适的填充模式。这是默认值。

适用场景:不关心具体适配策略的通用场景,将选择权交给框架。

模式切换 UI

Grid() {
  ForEach(this.fitLabels, (label: string, idx: number) => {
    GridItem() {
      Column() {
        Text(label)
          .fontSize(FontSize.CAPTION)
          .fontColor(this.fitMode === idx ? Color.White : AppColors.TEXT_TERTIARY)
          .fontWeight(this.fitMode === idx ? FontWeight.Medium : FontWeight.Normal)
          .padding({ left: 12, right: 12, top: 6, bottom: 6 })
          .backgroundColor(this.fitMode === idx ? '#1677FF' : '#F5F6FA')
          .borderRadius(14)
      }
    }
    .onClick(() => { this.fitMode = idx; })
  })
}
.columnsTemplate('1fr 1fr 1fr')

六种模式以3列网格布局展示,每个标签是一个可选中的胶囊按钮。当前选中的模式高亮为蓝色背景白色文字,未选中的为浅灰背景。点击标签即可切换填充模式,预览区实时更新。
在这里插入图片描述

属性调节面板

属性调节区使用四个 Slider 组件,分别控制圆角、透明度、边框和模糊:

圆角半径

Slider({
  value: this.cornerRadius,
  min: 0,
  max: 100,
  step: 2,
  style: SliderStyle.InSet
})
  .blockColor('#1677FF')
  .trackColor('#1677FF')
  .onChange((val: number) => { this.cornerRadius = val; })

圆角范围 0~100vp。当圆角设为 50vp 且图片容器宽高相等时,可以达到正圆形裁剪效果。实际上,将 borderRadius 设置为容器宽度的一半即可获得圆形图片——这是实现圆形头像的标准做法。

值得注意的是,borderRadius 不仅作用于图片,也作用于图片的边框(如果设置了的话),让边框同样呈现圆角。

透明度

Slider({
  value: this.imageOpacity,
  min: 0.1,
  max: 1.0,
  step: 0.05,
  style: SliderStyle.InSet
})
  .blockColor('#52C41A')
  .trackColor('#52C41A')
  .onChange((val: number) => { this.imageOpacity = val; })

透明度范围为 0.1~1.0,步进 0.05。我们设置最小值为 0.1 而非 0,因为完全透明的图片没有实用价值。透明度调节可以实现以下效果:

  • 水印效果:将透明度设为 0.2~0.3,图片可以作为背景水印
  • 淡化过渡:结合动画,实现图片的淡入淡出效果
  • 叠加层:低透明度图片配合上层文字,营造氛围感

边框宽度

Slider({
  value: this.imgBorderWidth,
  min: 0,
  max: 8,
  step: 1,
  style: SliderStyle.InSet
})
  .blockColor('#FAAD14')
  .trackColor('#FAAD14')
  .onChange((val: number) => { this.imgBorderWidth = val; })

边框宽度范围 0~8vp。边框配置通过 .border() 属性完成:

.border({
  width: this.imgBorderWidth,
  color: '#1677FF',
  style: BorderStyle.Solid
})

一个重要的命名注意事项:由于 borderWidth 是 ArkUI 组件的内置属性方法(用于设置组件边框宽度),我们不能将状态变量命名为 borderWidth,否则会与内置方法冲突。解决方案是取一个不同的名字,如 imgBorderWidth

模糊效果

Slider({
  value: this.blurRadius,
  min: 0,
  max: 10,
  step: 1,
  style: SliderStyle.InSet
})
  .blockColor('#722ED1')
  .trackColor('#722ED1')
  .onChange((val: number) => { this.blurRadius = val; })

.blur() 属性接受一个数字参数,表示高斯模糊的半径。数值越大,模糊效果越强烈。在我们的 Demo 中,范围为 0~10。

模糊效果的典型应用场景:

  • 背景模糊:将背景图模糊后作为页面背景,上方叠加清晰内容
  • 隐私保护:对敏感图片进行模糊处理,点击后才展示清晰版本
  • 加载状态:在图片完全加载前显示模糊的缩略图

图片来源切换

Demo 支持在本地资源和网络图片之间切换:

Button('🌐 网络图片')
  .backgroundColor(this.useNetwork ? '#1677FF' : '#F5F6FA')
  .fontColor(this.useNetwork ? Color.White : AppColors.TEXT_TERTIARY)
  .onClick(() => { this.useNetwork = true; })

Button('📱 本地资源')
  .backgroundColor(!this.useNetwork ? '#1677FF' : '#F5F6FA')
  .fontColor(!this.useNetwork ? Color.White : AppColors.TEXT_TERTIARY)
  .onClick(() => { this.useNetwork = false; })

两个按钮采用"互斥选中"的视觉设计:选中的按钮为蓝色实心,未选中的为浅灰。这与 Radio 的行为相似,但使用 Button 组件实现,灵活性更高。

当切换到网络图片时,Image 组件的 src 变为 this.currentUrl;切换到本地资源时,src 变为 $r('app.media.startIcon')

网络图片的 onError 处理

网络图片加载有一个不可忽视的问题:可能加载失败。原因包括:

  • 设备未连接网络
  • 图片 URL 已失效
  • 服务器证书问题
  • 图片格式不支持

我们通过 .onError() 回调来处理这种情况:

.onError(() => {
  AlertDialog.show({ message: '图片加载失败,请检查网络' });
})

当图片加载失败时,会弹出系统对话框提示用户。在生产项目中,更好的做法可能是显示一个预设的占位图:

Image(this.url)
  .onError(() => {
    this.imgSrc = $r('app.media.placeholder');
  })

模式对比一览

Demo 的第五个区域是一个对比网格,展示同一张本地图片在六种不同的 objectFit 模式下的表现:

Grid() {
  ForEach(this.fitLabels, (label: string, idx: number) => {
    GridItem() {
      Column() {
        Column() {
          Image($r('app.media.app_icon'))
            .width('100%')
            .height(70)
            .objectFit(this.fitValues[idx])
            .borderRadius(6)
        }
        .width('100%')
        .height(70)
        .backgroundColor('#F9FAFB')
        .border({ width: 1, color: '#E8ECF0' })

        Text(label)
          .fontSize(10)
          .fontColor(AppColors.TEXT_TERTIARY)
          .margin({ top: 4 })
      }
    }
  })
}
.columnsTemplate('1fr 1fr 1fr')

这个对比网格的设计价值在于:用户可以一次性看到所有模式在同一张图片上的效果差异。每种模式下的图片容器完全相同(width: 100%, height: 70vp),唯一变化的只是 objectFit 参数。这种"控制变量对比法"是帮助开发者理解 objectFit 行为的最佳方式。

每个对比项的视觉构成:

  • 外层 Column 限制容器尺寸
  • 浅灰底色 + 浅边框提供容器视觉边界
  • app_icon 作为测试图片(圆形图标,有助于观察各模式的裁切/缩放行为)
  • 底部标签文字标注模式名称

重置与刷新机制

参数重置

Button('↩ 重置参数')
  .onClick(() => {
    this.fitMode = 0;
    this.cornerRadius = 0;
    this.imageOpacity = 1.0;
    this.imgBorderWidth = 0;
    this.blurRadius = 0;
  })

重置按钮将所有参数恢复到默认值:Cover 模式、无圆角、完全透明、无边框、无模糊。这在用户"玩坏"了参数组合后非常有用——一键回到干净的状态。

更换图片

Button('🔄 更换网络图片')
  .onClick(() => {
    const seeds = ['mountain', 'ocean', 'forest', 'sunset', 'city',
      'garden', 'river', 'desert'];
    const seed = seeds[Math.floor(Math.random() * seeds.length)];
    this.currentUrl =
      `https://picsum.photos/seed/${seed}/${400 + Math.floor(Math.random() * 100)}/${300 + Math.floor(Math.random() * 50)}`;
    this.useNetwork = true;
  })

这个按钮展示了网络图片的灵活切换能力。我们使用 picsum.photos(一个随机图片服务)的 seed 机制来获取可复现的随机图片:

  • 8个不同的 seed 对应8种不同主题(山、海、森林、日落、城市、花园、河流、沙漠)
  • 随机尺寸让每次加载的图片宽高比略有差异,方便观察 objectFit 的效果差异
  • 点击后自动切换到网络图片模式

Image 组件的属性组合效应

一个有趣且重要的观察是:Image 的各项属性是同时生效且互相叠加的。例如:

  • borderRadius: 50 + border({ width: 4 }) → 边框也会呈现圆角
  • opacity: 0.5 + blur: 5 → 图片既半透明又模糊
  • objectFit: Cover + borderRadius: 50 → 图片被裁切填充的同时又被裁剪为圆形

理解这种叠加效应,可以帮助你在实际设计中灵活组合属性,创造出丰富的视觉效果。例如:

  • 圆形头像objectFit(Cover) + borderRadius(容器宽度/2) + border({ width: 2 })
  • 毛玻璃背景blur(8) + opacity(0.3)
  • 圆角卡片图objectFit(Cover) + borderRadius(12) + border({ width: 1, color: '#E0E0E0' })

Image 组件的注意事项

1. 状态变量命名冲突

正如前文所述,一些属性方法名(如 borderWidthopacityblur 等)不能用作状态变量名,否则会与组件继承的属性方法冲突。使用更具描述性的名称(如 imgBorderWidthimageOpacity)可以避免这个问题。

2. 网络图片的性能考量

网络图片的加载需要时间。对于列表中的大量缩略图,建议:

  • 使用图片懒加载(仅在图片进入可视区域时开始加载)
  • 使用适当尺寸的缩略图而非原图
  • 考虑使用缓存策略减少重复加载

3. 本地资源的路径格式

本地图片必须放置在 resources/base/media/ 目录下,引用方式为 $r('app.media.xxx'),其中 xxx 是不带扩展名的文件名。以下引用方式是错误的:

// 错误:不能使用相对路径
Image('./assets/image.png')

// 错误:不能使用绝对路径
Image('/data/storage/image.png')

// 正确
Image($r('app.media.app_icon'))

4. 模糊性能

.blur() 是一种实时渲染效果,较大的模糊半径(如超过20)可能会影响滚动性能。对于需要高性能的列表场景,建议预先准备模糊后的图片资源,而非在组件层面实时模糊。

总结

Image 组件是 ArkUI 中使用频率最高的组件之一。本文通过一个"图片美化工具"Demo,系统展示了 Image 组件的五项核心可调节属性:

  • objectFit:六种填充模式(Cover/Contain/Fill/None/ScaleDown/Auto),各自适用于不同的布局需求
  • borderRadius:圆角裁剪,从方角到圆角到正圆,灵活调节
  • opacity:透明度调节,范围 0~1,用于水印、淡化、叠加等效果
  • border:边框样式,包括宽度、颜色、线型,可与圆角协同工作
  • blur:高斯模糊,营造景深和隐私保护等效果

Demo 的四个交互点覆盖了图片美化的完整工作流:

  1. 模式切换:6种 objectFit 模式的实时切换与对比
  2. 属性调节:四个 Slider 分别控制圆角、透明度、边框、模糊
  3. 来源切换:本地资源与网络图片的无缝切换
  4. 重置与刷新:一键恢复默认参数,随机更换网络图片

Image 组件的强大之处在于,它将原本需要图像处理库才能实现的效果(模糊、裁剪、透明度叠加)内建到了声明式 UI 的属性层面,让开发者能够以极低的代码成本实现丰富的视觉效果。将本文的"属性组合"思路扩展到实际项目中,你可以构建出更加精致、美观的图片展示体验。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐