找回密码
 立即注册
首页 业界区 业界 第十六节:图片展示组件Image核心讲解与实战(基础篇) ...

第十六节:图片展示组件Image核心讲解与实战(基础篇)

磁呃泵 2 小时前
第十六节:图片展示组件Image核心讲解与实战(基础篇)

【学习目标】


  • 理解Image组件的核心定位,掌握png/jpg/svg/gif等主流格式的支持特性,明确各类格式的适配场景;
  • 精通本地资源、网络图片、媒体库、Base64等基础数据源的加载方式与权限配置;
  • 掌握objectFit、interpolation、renderMode等核心属性的用法,能根据业务需求自定义基础图片展示效果。
一、Image组件核心认知

1.1 组件定义与定位

Image是鸿蒙图片展示的核心基础组件,负责将不同来源、不同格式的图片资源渲染到界面上,是应用开发中高频使用的组件之一。它承接了从图片加载、解码到渲染展示的全流程能力,同时支持丰富的属性配置和进阶扩展,可满足日常静态展示、动态效果呈现、性能优化等多场景需求。
1.2 支持图片格式与适配说明

支持格式格式后缀适用场景特殊说明位图基础格式png、jpg、bmp、jpeg日常静态图片展示(图标、背景、内容图)png支持透明通道,适合图标/按钮;jpg压缩率高,适合大尺寸内容图矢量图格式svg自适应缩放场景(LOGO、简易图标、插画)无像素失真,支持动态修改颜色,无原始尺寸时需手动设置宽高动态图片格式gif、heif短动态效果展示(表情包、简易动画)支持多帧播放,ArkTS卡片中gif仅播放一次不支持格式apng、svga-如需实现复杂动态效果,可通过AnimatedDrawableDescriptor1.3 典型适用场景

场景类型具体示例核心实现要点基础静态展示页面图标、背景图、商品图片本地资源/Resource加载 + objectFit适配 + 基础样式配置网络图片展示新闻配图、头像、远程资源图网络路径加载 + INTERNET权限 + 缓存优化 + 加载/失败占位自适应矢量展示应用LOGO、功能图标、导航栏图标SVG格式 + fillColor动态改色 + 固定宽高配置动态效果展示表情包、操作反馈动画、轮播小动画GIF/HEIF格式 + AnimatedDrawableDescriptor + 循环/时长配置复杂定制展示圆形头像、带徽章图片、分层背景objectFit(Cover) + 裁剪 + LayeredDrawableDescriptor分层叠加性能优化场景长列表图片、大尺寸图片展示sourceSize解码降分辨率 + 异步加载 + 缓存策略二、工程结构与环境准备

2.1 工程基础配置


  • 基础环境:鸿蒙API 12+、Stage模型
  • 权限声明:网络图片需在module.json5中添加ohos.permission.INTERNET;媒体库访问需申请ohos.permission.READ_MEDIA_IMAGES权限(PhotoViewPicker安全控件方式无需声明)
2.2 工程目录结构
  1. ImageApplication/
  2. ├── AppScope/
  3. │   └── app.json5  // 应用全局配置
  4. ├── entry/
  5. │   ├── src/
  6. │   │   ├── main/
  7. │   │   │   ├── ets/
  8. │   │   │   │   ├── entryability/
  9. │   │   │   │   │   └── EntryAbility.ets  // 应用入口
  10. │   │   │   │   ├── pages/  // 功能演示页面
  11. │   │   │   │   │   ├── Index.ets  // 导航主页面
  12. │   │   │   │   │   ├── ImageBasicPage.ets  // 基础用法&核心属性
  13. │   │   │   │   │   ├── ImageSourcePage.ets // 多数据源加载
  14. │   │   │   │   │   ├── Base64ImagePage.ets // Base64图片加载
  15. │   │   │   │   │   ├── ImageMediaLibrary.ets // 媒体库加载
  16. │   │   │   ├── resources/
  17. │   │   │   │   ├── media/  // $r引用资源
  18. │   │   │   │   └── rawfile/ // $rawfile引用原生资源
  19. │   │   │   └── module.json5  // 权限声明
复制代码
2.3 导航主页面(Index.ets)
  1. import { router } from '@kit.ArkUI';
  2. interface RouterButton {
  3.   title: string;
  4.   url: string;
  5. }
  6. @Entry
  7. @Component
  8. struct Index {
  9.   private buttonList: RouterButton[] = [
  10.     { title: "Image基础用法&核心属性", url: 'pages/ImageBasicPage' },
  11.     { title: "Image多数据源加载", url: 'pages/ImageSourcePage' },
  12.     { title: "Base64图片加载", url: 'pages/Base64ImagePage' },
  13.     { title: "MediaLibrary图片加载", url: 'pages/ImageMediaLibrary' },
  14.   ];
  15.   build() {
  16.     Column({ space: 15 }) {
  17.       Text("Image组件实战教程")
  18.         .fontSize(30)
  19.         .fontWeight(FontWeight.Bold)
  20.         .margin({ bottom: 40 })
  21.         .textAlign(TextAlign.Center);
  22.       ForEach(
  23.         this.buttonList,
  24.         (item: RouterButton) => {
  25.           Button(item.title)
  26.             .width('80%')
  27.             .height(45)
  28.             .backgroundColor($r('sys.color.brand'))
  29.             .fontColor(Color.White)
  30.             .fontSize(16)
  31.             .borderRadius(8)
  32.             .onClick(() => {
  33.               router.pushUrl({
  34.                 url: item.url,
  35.                 params: { title: item.title }
  36.               });
  37.             })
  38.         },
  39.         (item:RouterButton) => item.url
  40.       );
  41.     }
  42.     .width('100%')
  43.     .height('100%')
  44.     .justifyContent(FlexAlign.Center)
  45.     .backgroundColor('#F5F5F5')
  46.     .padding(20);
  47.   }
  48. }
复制代码
三、核心属性与实战用法

3.1 必配属性(避免布局异常)
  1. Image(数据源)
  2.   .width(数值)
  3.   .height(数值)
  4.   .objectFit(ImageFit.Contain)
复制代码
3.2 objectFit(最核心)

枚举值通俗描述视觉效果适用场景Contain完整显示图片完整,容器可能留白图标、LOGO、需完整展示的内容Cover填充裁剪容器无空白,图片裁剪头像、背景图、填满容器的图片Auto自适应同Contain默认场景Fill拉伸填充图片变形慎用ScaleDown缩小不放大小图不变,大图缩小小图防模糊3.3 其他核心属性


  • interpolation:图片放大插值,抗锯齿
  • objectRepeat:图片重复平铺
  • renderMode + fillColor:SVG图标改色
  • borderRadius:圆角、圆形头像
  • alt:加载失败/加载中占位图
3.4 基础属性实战页面(ImageBasicPage.ets)
  1. import { LengthMetrics } from '@kit.ArkUI';
  2. // 1. 定义ImageFit配置项的接口
  3. interface ImageFitItem {
  4.   name: string;        // 枚举名称(用于展示)
  5.   desc: string;        // 枚举说明(用于展示)
  6.   value: ImageFit;     // ImageFit枚举值
  7. }
  8. @Entry
  9. @Component
  10. struct ImageBasicPage {
  11.   private imageFitList: ImageFitItem[] = [
  12.     { name: 'Contain', desc: '完整显示', value: ImageFit.Contain },
  13.     { name: 'Cover', desc: '填充裁剪', value: ImageFit.Cover },
  14.     { name: 'Auto', desc: '自适应', value: ImageFit.Auto },
  15.     { name: 'Fill', desc: '拉伸填充', value: ImageFit.Fill },
  16.     { name: 'ScaleDown', desc: '缩小不放大', value: ImageFit.ScaleDown }
  17.   ];
  18.   build() {
  19.     Scroll() {
  20.       Column({ space: 25 }) {
  21.         // 页面标题
  22.         Text("Image核心属性演示")
  23.           .fontSize(24)
  24.           .fontWeight(FontWeight.Bold)
  25.           .width('100%')
  26.           .textAlign(TextAlign.Center);
  27.         // 1. objectFit枚举演示
  28.         Text("1. objectFit核心缩放枚举")
  29.           .fontSize(16)
  30.           .fontWeight(FontWeight.Medium)
  31.           .width('90%')
  32.           .textAlign(TextAlign.Start);
  33.         // Flex布局:自动换行,统一间距
  34.         Flex({
  35.           wrap: FlexWrap.Wrap,
  36.           justifyContent: FlexAlign.Center,
  37.           alignItems: ItemAlign.Center,
  38.           space:{
  39.             main:LengthMetrics.vp(10),
  40.             cross:LengthMetrics.vp(10)
  41.           }
  42.         }) {
  43.           // ForEach批量渲染
  44.           ForEach(
  45.             this.imageFitList,
  46.             (item: ImageFitItem) => { // 显式指定类型,提升可读性
  47.               Column({ space: 8 }) {
  48.                 Text(`${item.name}\n(${item.desc})`)
  49.                   .fontSize(16)
  50.                   .fontColor('#666')
  51.                   .textAlign(TextAlign.Center)
  52.                 Image($r('app.media.banner'))
  53.                   .width(160)
  54.                   .height(120)
  55.                   .objectFit(item.value) // 类型安全:只能传ImageFit枚举值
  56.                   .backgroundColor(Color.Pink)
  57.                   .border({ width: 1, color: '#eee' });
  58.               }
  59.               .flexGrow(1)
  60.               .flexShrink(1);
  61.             },
  62.             (item:ImageFitItem) => item.name // 唯一标识
  63.           );
  64.         }
  65.         .width('100%')
  66.         .padding({ left: 10, right: 10 });
  67.         // 2. 圆角图片
  68.         Text("2. 圆角头像(borderRadius + Cover)")
  69.           .fontSize(16)
  70.           .width('90%')
  71.           .textAlign(TextAlign.Start);
  72.         Image($r('app.media.avatar'))
  73.           .width(120)
  74.           .height(120)
  75.           .objectFit(ImageFit.Cover)
  76.           .borderRadius(20)
  77.           .backgroundColor('#f5f5f5');
  78.         // 3. 图标动态改色
  79.         Text("3. 图标改色(Template + fillColor)")
  80.           .fontSize(16)
  81.           .width('90%')
  82.           .textAlign(TextAlign.Start);
  83.         Row(){
  84.           Image($r('app.media.icon_setting'))
  85.             .width(40)
  86.             .height(40)
  87.             .backgroundColor('#999');
  88.           Image($r('app.media.icon_setting'))
  89.             .width(40)
  90.             .height(40)
  91.             .renderMode(ImageRenderMode.Template)
  92.             .fillColor(Color.Blue)
  93.             .backgroundColor('#999');
  94.         }.width('100%')
  95.         .justifyContent(FlexAlign.Center)
  96.         // 4. 图片重复
  97.         Text("4. objectRepeat:XY(双向重复)")
  98.           .fontSize(16)
  99.           .width('90%')
  100.           .textAlign(TextAlign.Start);
  101.         Image($r('app.media.love'))
  102.           .width(200)
  103.           .height(200)
  104.           .objectRepeat(ImageRepeat.XY)
  105.           .objectFit(ImageFit.ScaleDown)
  106.           .backgroundColor(Color.Pink)
  107.           .overlay('ImageRepeat.XY', { align: Alignment.Bottom, offset: { x: 0, y: 20 } });
  108.         // 5. 放大抗锯齿
  109.         Text("5. interpolation:High(放大抗锯齿)")
  110.           .fontSize(16)
  111.           .width('90%')
  112.           .textAlign(TextAlign.Start);
  113.         Row(){
  114.           Image($r('app.media.love'))
  115.             .width(100)
  116.             .height(100)
  117.             .interpolation(ImageInterpolation.None)
  118.             .backgroundColor('#f5f5f5');
  119.           Image($r('app.media.love'))
  120.             .width(100)
  121.             .height(100)
  122.             .interpolation(ImageInterpolation.High)
  123.             .backgroundColor('#f5f5f5');
  124.         }.width('100%')
  125.         .justifyContent(FlexAlign.Center)
  126.       }
  127.       .backgroundColor('#F5F5F5')
  128.       .width('100%')
  129.     }
  130.     .width('100%')
  131.     .height('100%')
  132.   }
  133. }
复制代码
运行效果

1.gif

四、多数据源加载

4.1 本地资源加载

(1)media 目录($r)
  1. Image($r('app.media.harmonyOS'))
复制代码
(2)rawfile 目录($rawfile)
  1. Image($rawfile('development.webp'))
复制代码
4.2 网络图片加载

步骤1:声明权限(module.json5)
  1. {
  2.   "module": {
  3.     "requestPermissions": [
  4.       { "name": "ohos.permission.INTERNET" }
  5.     ]
  6.   }
  7. }
复制代码
步骤2:加载网络图片
  1. Image('https://res.vmallres.com/FssCdnProxy/vmall_product_uom/pmsCdn/09EFE73EA18A31D59FF3D60F7F54566A.jpg')
  2.   .width(300)
  3.   .objectFit(ImageFit.Contain)
复制代码
4.3 多数据源页面(ImageSourcePage.ets)
  1. @Entry
  2. @Component
  3. struct ImageSourcePage {
  4.   build() {
  5.     Column({ space: 15 }) {
  6.       // 1. Resource资源
  7.       Text("1. Resource资源($r)")
  8.         .fontSize(16)
  9.         .width('90%')
  10.         .textAlign(TextAlign.Start);
  11.       Image($r('app.media.harmonyOS'))
  12.         .width(300)
  13.         .aspectRatio(16/9)
  14.         .objectFit(ImageFit.Contain)
  15.         .backgroundColor('#f5f5f5');
  16.       // 2. rawfile资源
  17.       Text("2. rawfile资源($rawfile)")
  18.         .fontSize(16)
  19.         .width('90%')
  20.         .textAlign(TextAlign.Start);
  21.       Image($rawfile('development.webp'))
  22.         .width(300)
  23.         .aspectRatio(16/9)
  24.         .objectFit(ImageFit.Cover)
  25.         .backgroundColor('#f5f5f5');
  26.       // 3. 网络资源
  27.       Text("3. 网络资源(URL)")
  28.         .fontSize(16)
  29.         .width('90%')
  30.         .textAlign(TextAlign.Start);
  31.       Image('https://res.vmallres.com/FssCdnProxy/vmall_product_uom/pmsCdn/09EFE73EA18A31D59FF3D60F7F54566A.jpg')
  32.         .width(300)
  33.         .syncLoad(false) // 异步加载(默认值,避免阻塞UI)
  34.         .objectFit(ImageFit.Contain)
  35.         .backgroundColor('#f5f5f5');
  36.     }
  37.     .width('100%')
  38.     .height('100%')
  39.     .backgroundColor('#F5F5F5')
  40.     .justifyContent(FlexAlign.Center);
  41.   }
  42. }
复制代码
运行效果

2.jpeg

4.4 媒体库图片加载(ImageMediaLibrary.ets)
  1. import { photoAccessHelper } from '@kit.MediaLibraryKit';
  2. import { BusinessError } from '@kit.BasicServicesKit';
  3. @Entry
  4. @Component
  5. struct ImageMediaLibrary {
  6.   @State imgUris: string[] = [];
  7.   // 获取图库图片URI
  8.   getAllImages() {
  9.     try {
  10.       const selectOptions = new photoAccessHelper.PhotoSelectOptions();
  11.       selectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
  12.       selectOptions.maxSelectNumber = 3; // 最多选择3张
  13.       const photoPicker = new photoAccessHelper.PhotoViewPicker();
  14.       photoPicker.select(selectOptions)
  15.         .then((result) => {
  16.           this.imgUris = result.photoUris; // 存储图片URI
  17.         })
  18.         .catch((err: BusinessError) => {
  19.           console.error(`图库选择失败:${err.code} - ${err.message}`);
  20.         });
  21.     } catch (err) {
  22.       console.error(`图库访问异常:${err.message}`);
  23.     }
  24.   }
  25.   build() {
  26.     Column({ space: 20 }) {
  27.       Button("选择图库图片")
  28.         .onClick(() => this.getAllImages())
  29.         .margin({ top: 20 });
  30.       // 展示选中的图片
  31.       ForEach(
  32.         this.imgUris,
  33.         (uri: string) => {
  34.           Image(uri)
  35.             .width(120)
  36.             .height(120)
  37.             .objectFit(ImageFit.Cover)
  38.             .borderRadius(8)
  39.             .backgroundColor('#f5f5f5');
  40.         },
  41.         (uri: string) => uri
  42.       );
  43.     }
  44.     .width('100%')
  45.     .height('100%')
  46.     .backgroundColor('#F5F5F5');
  47.   }
  48. }
复制代码
运行效果

3.gif

4.5 Base64 图片加载(Base64ImagePage.ets)
  1. import util from '@ohos.util';
  2. @Entry
  3. @Component
  4. struct Base64ImagePage {
  5.   @State base64image: string = ''
  6.   aboutToAppear(): void {
  7.     // base64 字符串由于很长,所以我们将图片并转换为Base64字符串并加载
  8.     this.base64image = this.imageToBase64('harmonyOSCar.jpeg')
  9.   }
  10.   /**
  11.    * 读取rawfile图片并转换为Base64字符串
  12.    * @param imageName rawfile下的图片文件名(含后缀)
  13.    * @returns 标准Base64图片字符串
  14.    */
  15.   imageToBase64(imageName: string): string {
  16.     try {
  17.       // 1. 获取资源管理器
  18.       const uiContext = this.getUIContext();
  19.       const hostContext = uiContext.getHostContext();
  20.       const resMgr = hostContext?.resourceManager;
  21.       if (!resMgr) {
  22.         console.error('获取resourceManager失败');
  23.         return '';
  24.       }
  25.       // 2. 同步读取rawfile文件内容
  26.       const imageData = resMgr.getRawFileContentSync(imageName)
  27.       if (!imageData || imageData.length === 0) {
  28.         console.error('读取图片数据为空');
  29.         return '';
  30.       }
  31.       // 3. 转换为Base64字符串(确保入参类型正确)
  32.       const base64Helper = new util.Base64Helper();
  33.       const base64Str = base64Helper.encodeToStringSync(imageData);
  34.       if (!base64Str) {
  35.         console.error('Base64编码失败');
  36.         return '';
  37.       }
  38.       // 4. 动态生成正确的MIME前缀(根据文件后缀匹配)
  39.       const ext = imageName.split('.').pop()?.toLowerCase() || 'png';
  40.       // 映射常见图片格式的MIME类型
  41.       const mimeMap: Record<string, string> = {
  42.         'png': 'image/png',
  43.         'jpg': 'image/jpeg',
  44.         'jpeg': 'image/jpeg',
  45.         'webp': 'image/webp',
  46.         'gif': 'image/gif'
  47.       };
  48.       const mimeType = mimeMap[ext] || 'image/png';
  49.       // 5. 拼接标准Base64图片格式
  50.       const base64ImageStr = `data:${mimeType};base64,${base64Str}`;
  51.       console.log('生成的Base64字符串:', base64ImageStr.substring(0, 50) + '...'); // 打印前50位验证
  52.       return base64ImageStr;
  53.     } catch (error) {
  54.       console.error('图片转Base64失败:', error);
  55.       return '';
  56.     }
  57.   }
  58.   build() {
  59.     Column({ space: 15 }) {
  60.       Text("Base64格式图片展示")
  61.         .fontSize(16)
  62.         .width('90%')
  63.         .textAlign(TextAlign.Start);
  64.       Image(this.base64image)
  65.         .width(200)
  66.         .height(200)
  67.         .objectFit(ImageFit.Contain)
  68.         .backgroundColor('#f5f5f5');
  69.     }
  70.     .width('100%')
  71.     .height('100%')
  72.     .backgroundColor('#F5F5F5')
  73.     .justifyContent(FlexAlign.Center);
  74.   }
  75. }
复制代码
运行效果

4.png

4.6 数据源选型建议

数据源类型适用场景优势$r本地图标、常规图片适配强、使用简单$rawfile特殊格式、原始文件保留原始结构网络URL远程图片、头像、内容图动态更新、无需打包进应用媒体库URI相册选择图片安全、无需权限Base64小图标、内嵌图片无网络请求、加载快五、内容总结


  • 核心定位:Image是鸿蒙系统最常用的图片展示组件,支持png、jpg、svg、gif等主流格式,可满足静态展示、圆角头像、图标改色等日常开发需求。
  • 必掌握属性:width/height必须显式设置;objectFit是图片缩放核心,优先使用Contain和Cover;borderRadius实现圆角/圆形效果;renderMode+fillColor用于SVG图标改色。
  • 多数据源加载:本地资源用$r/$rawfile;网络图片需配置INTERNET权限;媒体库图片使用PhotoViewPicker无需申请权限;Base64适合小型图标内嵌展示。
  • 开发规范:网络图片建议配置alt占位图,媒体库选择优先使用系统控件,避免权限风险与异常崩溃。
六、代码仓库


  • 工程名称:ImageApplication
  • 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
七、下节预告

下一节我们将学习 第十七节:Image组件进阶实战,内容包括:

  • PixelMap 像素级图片处理
  • 图片加载事件与异常兜底
  • 分层图片、GIF 动画合成与播放
  • 图片性能优化与内存管理

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册