1# 显示图片 (Image)
2
3
4开发者经常需要在应用中显示一些图片,例如:按钮中的icon、网络图片、本地图片等。在应用中显示图片需要使用Image组件实现,Image支持多种图片格式,包括png、jpg、bmp、svg、gif和heif,具体用法请参考[Image](../reference/apis-arkui/arkui-ts/ts-basic-components-image.md)组件。
5
6
7Image通过调用接口来创建,接口调用形式如下:
8
9```ts
10Image(src: PixelMap | ResourceStr | DrawableDescriptor)
11```
12
13
14该接口通过图片数据源获取图片,支持本地图片和网络图片的渲染展示。其中,src是图片的数据源,加载方式请参考[加载图片资源](#加载图片资源)。
15
16
17## 加载图片资源
18
19Image支持加载存档图、多媒体像素图两种类型。
20
21
22### 存档图类型数据源
23
24存档图类型的数据源可以分为本地资源、网络资源、Resource资源、媒体库资源和base64。
25
26- 本地资源
27
28  创建文件夹,将本地图片放入ets文件夹下的任意位置。
29
30  Image组件引入本地图片路径,即可显示图片(根目录为ets文件夹)。
31
32  ```ts
33  Image('images/view.jpg')
34  .width(200)
35  ```
36
37- 网络资源
38
39  引入网络图片需申请权限ohos.permission.INTERNET,具体申请方式请参考[声明权限](../security/AccessToken/declare-permissions.md)。此时,Image组件的src参数为网络图片的链接。
40
41  当前Image组件仅支持加载简单网络图片。
42
43  Image组件首次加载网络图片时,需要请求网络资源,非首次加载时,默认从缓存中直接读取图片,更多图片缓存设置请参考[setImageCacheCount](../reference/apis-arkui/js-apis-system-app.md#setimagecachecount7)、[setImageRawDataCacheSize](../reference/apis-arkui/js-apis-system-app.md#setimagerawdatacachesize7)、[setImageFileCacheSize](../reference/apis-arkui/js-apis-system-app.md#setimagefilecachesize7)。但是,这三个图片缓存接口并不灵活,且后续不继续演进,对于复杂情况,更推荐使用[ImageKnife](https://gitee.com/openharmony-tpc/ImageKnife)44
45  API version 14及之后,Image组件在显示网络图片时,网络图片下载与缓存能力将不再内嵌于Image组件中,而是剥离至上传下载模块进行统一管理。上传下载模块提供独立的预下载接口,允许应用开发者在创建Image组件前预下载所需图片。组件创建后,通过向上传下载模块请求数据,从而优化了Image组件的显示流程。关于网络缓存的位置,对于API version 14之前的版本,Image组件的缓存位于应用的本地沙箱路径下,而对于API version 14及之后的版本,缓存则移至应用根目录下的cache目录中。
46
47  ```ts
48  Image('https://www.example.com/example.JPG') // 实际使用时请替换为真实地址
49  ```
50
51- Resource资源
52
53  使用资源格式可以跨包/跨模块引入图片,resources文件夹下的图片都可以通过$r资源接口读取到并转换到Resource格式。
54
55  **图1** resources  
56
57  ![image-resource](figures/image-resource.jpg)
58
59  调用方式:
60
61  ```
62  Image($r('app.media.icon'))
63  ```
64
65  还可以将图片放在rawfile文件夹下。
66
67  **图2** rawfile  
68
69  ![image-rawfile](figures/image-rawfile.jpg)
70
71  调用方式:
72
73  ```
74  Image($rawfile('example1.png'))
75  ```
76
77- 媒体库file://data/storage
78
79  支持file://路径前缀的字符串,用于访问通过[选择器](../reference/apis-core-file-kit/js-apis-file-picker.md)提供的图片路径。
80
81  1. 调用接口获取图库的照片url。
82
83      ```ts
84      import { photoAccessHelper } from '@kit.MediaLibraryKit';
85      import { BusinessError } from '@kit.BasicServicesKit';
86
87      @Entry
88      @Component
89      struct Index {
90        @State imgDatas: string[] = [];
91        // 获取照片url集
92        getAllImg() {
93          try {
94            let PhotoSelectOptions:photoAccessHelper.PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
95            PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
96            PhotoSelectOptions.maxSelectNumber = 5;
97            let photoPicker:photoAccessHelper.PhotoViewPicker = new photoAccessHelper.PhotoViewPicker();
98            photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult:photoAccessHelper.PhotoSelectResult) => {
99              this.imgDatas = PhotoSelectResult.photoUris;
100              console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult));
101            }).catch((err:Error) => {
102              let message = (err as BusinessError).message;
103              let code = (err as BusinessError).code;
104              console.error(`PhotoViewPicker.select failed with. Code: ${code}, message: ${message}`);
105            });
106          } catch (err) {
107            let message = (err as BusinessError).message;
108            let code = (err as BusinessError).code;
109            console.error(`PhotoViewPicker failed with. Code: ${code}, message: ${message}`);    }
110        }
111
112        // aboutToAppear中调用上述函数,获取图库的所有图片url,存在imgDatas中
113        async aboutToAppear() {
114          this.getAllImg();
115        }
116        // 使用imgDatas的url加载图片。
117        build() {
118          Column() {
119            Grid() {
120              ForEach(this.imgDatas, (item:string) => {
121                GridItem() {
122                  Image(item)
123                    .width(200)
124                }
125              }, (item:string):string => JSON.stringify(item))
126            }
127          }.width('100%').height('100%')
128        }
129      }
130      ```
131
132  2. 从媒体库获取的url格式通常如下。
133
134      ```ts
135      Image('file://media/Photos/5')
136      .width(200)
137      ```
138
139
140- base64
141
142  路径格式为data:image/[png|jpeg|bmp|webp|heif];base64,[base64 data],其中[base64 data]为Base64字符串数据。
143
144  Base64格式字符串可用于存储图片的像素数据,在网页上使用较为广泛。
145
146
147### 多媒体像素图
148
149PixelMap是图片解码后的像素图,具体用法请参考[图片开发指导](../media/image/image-overview.md)。以下示例将加载的网络图片返回的数据解码成PixelMap格式,再显示在Image组件上。
150
1511. 创建PixelMap状态变量。
152
153   ```ts
154   @State image: PixelMap | undefined = undefined;
155   ```
156
1572. 引用多媒体。
158
159   (1) 引用网络权限与媒体库权限。
160
161   ```ts
162   import { http } from '@kit.NetworkKit';
163   import { image } from '@kit.ImageKit';
164   import { BusinessError } from '@kit.BasicServicesKit';
165   ```
166
167   (2) 填写网络图片地址。
168
169   ```ts
170   let OutData: http.HttpResponse
171   http.createHttp().request("https://www.example.com/xxx.png",
172     (error: BusinessError, data: http.HttpResponse) => {
173       if (error) {
174         console.error(`http request failed with. Code: ${error.code}, message: ${error.message}`);
175       } else {
176         OutData = data
177       }
178     }
179   )
180   ```
181
1823. 将网络地址成功返回的数据,编码转码成pixelMap的图片格式。
183
184   ```ts
185   let code: http.ResponseCode | number = OutData.responseCode
186   if (http.ResponseCode.OK === code) {
187     let imageData: ArrayBuffer = OutData.result as ArrayBuffer;
188     let imageSource: image.ImageSource = image.createImageSource(imageData);
189
190     class tmp {
191       height: number = 100
192       width: number = 100
193     }
194
195     let si: tmp = new tmp()
196     let options: Record<string, number | boolean | tmp> = {
197       'alphaType': 0, // 透明度
198       'editable': false, // 是否可编辑
199       'pixelFormat': 3, // 像素格式
200       'scaleMode': 1, // 缩略值
201       'size': { height: 100, width: 100 }
202     } // 创建图片大小
203
204     class imagetmp {
205       image: PixelMap | undefined = undefined
206       set(val: PixelMap) {
207         this.image = val
208       }
209     }
210
211     imageSource.createPixelMap(options).then((pixelMap: PixelMap) => {
212       let im = new imagetmp()
213       im.set(pixelMap)
214     })
215   }
216   ```
217
2184. 显示图片。
219
220   ```ts
221   class htp{
222     httpRequest: Function | undefined = undefined
223     set(){
224       if(this.httpRequest){
225         this.httpRequest()
226       }
227     }
228   }
229   Button("获取网络图片")
230     .onClick(() => {
231       let sethtp = new htp()
232       sethtp.set()
233     })
234   Image(this.image).height(100).width(100)
235   ```
236
237   同时,也可以传入pixelMap创建[PixelMapDrawableDescriptor](../reference/apis-arkui/js-apis-arkui-drawableDescriptor.md#pixelmapdrawabledescriptor12)对象,用来显示图片。
238
239   ```ts
240   import { DrawableDescriptor, PixelMapDrawableDescriptor } from '@kit.ArkUI'
241   class htp{
242     httpRequest: Function | undefined = undefined
243     set(){
244       if(this.httpRequest){
245         this.httpRequest()
246       }
247     }
248   }
249   Button("获取网络图片")
250     .onClick(() => {
251       let sethtp = new htp()
252       sethtp.set()
253       this.drawablePixelMap = new PixelMapDrawableDescriptor(this.image)
254     })
255   Image(this.drawablePixelMap).height(100).width(100)
256   ```
257
258## 显示矢量图
259
260Image组件可显示矢量图(svg格式的图片),svg标签文档请参考[svg说明](../../application-dev/reference/apis-arkui/arkui-ts/ts-basic-svg.md)。
261
262svg格式的图片可以使用fillColor属性改变图片的绘制颜色。
263
264
265```ts
266Image($r('app.media.cloud'))
267  .width(50)
268  .fillColor(Color.Blue)
269```
270
271  **图3** 原始图片  
272
273![屏幕截图_20230223_141141](figures/屏幕截图_20230223_141141.png)
274
275  **图4** 设置绘制颜色后的svg图片  
276
277![屏幕截图_20230223_141404](figures/屏幕截图_20230223_141404.png)
278
279### 矢量图引用位图
280
281如果Image加载的Svg图源中包含对本地位图的引用,则Svg图源的路径应当设置为以ets为根目录的工程路径,同时,本地位图的路径应设置为与Svg图源同级的相对路径。
282
283Image加载的Svg图源路径设置方法如下所示:
284
285```ts
286Image("images/icon.svg")
287  .width(50)
288  .height(50)
289```
290Svg图源通过`<image>`标签的`xlink:href`属性指定本地位图路径,本地位图路径设置为跟Svg图源同级的相对路径:
291
292```
293<svg width="200" height="200">
294  <image width="200" height="200" xlink:href="sky.png"></image>
295</svg>
296```
297文件工程路径示例如图:
298
299![image path](figures/imagePath.png)
300
301## 添加属性
302
303给Image组件设置属性可以使图片显示更灵活,达到一些自定义的效果。以下是几个常用属性的使用示例,完整属性信息详见[Image](../reference/apis-arkui/arkui-ts/ts-basic-components-image.md)。
304
305### 设置图片缩放类型
306
307通过objectFit属性使图片缩放到高度和宽度确定的框内。
308
309
310```ts
311@Entry
312@Component
313struct MyComponent {
314  scroller: Scroller = new Scroller()
315
316  build() {
317    Scroll(this.scroller) {
318      Column() {
319        Row() {
320          Image($r('app.media.img_2'))
321            .width(200)
322            .height(150)
323            .border({ width: 1 })
324              // 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
325            .objectFit(ImageFit.Contain)
326            .margin(15)
327            .overlay('Contain', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
328          Image($r('app.media.ic_img_2'))
329            .width(200)
330            .height(150)
331            .border({ width: 1 })
332              // 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
333            .objectFit(ImageFit.Cover)
334            .margin(15)
335            .overlay('Cover', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
336          Image($r('app.media.img_2'))
337            .width(200)
338            .height(150)
339            .border({ width: 1 })
340              // 自适应显示。
341            .objectFit(ImageFit.Auto)
342            .margin(15)
343            .overlay('Auto', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
344        }
345
346        Row() {
347          Image($r('app.media.img_2'))
348            .width(200)
349            .height(150)
350            .border({ width: 1 })
351              // 不保持宽高比进行放大缩小,使得图片充满显示边界。
352            .objectFit(ImageFit.Fill)
353            .margin(15)
354            .overlay('Fill', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
355          Image($r('app.media.img_2'))
356            .width(200)
357            .height(150)
358            .border({ width: 1 })
359              // 保持宽高比显示,图片缩小或者保持不变。
360            .objectFit(ImageFit.ScaleDown)
361            .margin(15)
362            .overlay('ScaleDown', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
363          Image($r('app.media.img_2'))
364            .width(200)
365            .height(150)
366            .border({ width: 1 })
367              // 保持原有尺寸显示。
368            .objectFit(ImageFit.None)
369            .margin(15)
370            .overlay('None', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
371        }
372      }
373    }
374  }
375}
376```
377
378![zh-cn_image_0000001622804833](figures/zh-cn_image_0000001622804833.png)
379
380
381### 图片插值
382
383当原图分辨率较低并且放大显示时,图片会模糊出现锯齿。这时可以使用interpolation属性对图片进行插值,使图片显示得更清晰。
384
385
386```ts
387@Entry
388@Component
389struct Index {
390  build() {
391    Column() {
392      Row() {
393        Image($r('app.media.grass'))
394          .width('40%')
395          .interpolation(ImageInterpolation.None)
396          .borderWidth(1)
397          .overlay("Interpolation.None", { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
398          .margin(10)
399        Image($r('app.media.grass'))
400          .width('40%')
401          .interpolation(ImageInterpolation.Low)
402          .borderWidth(1)
403          .overlay("Interpolation.Low", { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
404          .margin(10)
405      }.width('100%')
406      .justifyContent(FlexAlign.Center)
407
408      Row() {
409        Image($r('app.media.grass'))
410          .width('40%')
411          .interpolation(ImageInterpolation.Medium)
412          .borderWidth(1)
413          .overlay("Interpolation.Medium", { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
414          .margin(10)
415        Image($r('app.media.grass'))
416          .width('40%')
417          .interpolation(ImageInterpolation.High)
418          .borderWidth(1)
419          .overlay("Interpolation.High", { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
420          .margin(10)
421      }.width('100%')
422      .justifyContent(FlexAlign.Center)
423    }
424    .height('100%')
425  }
426}
427```
428
429![zh-cn_image_0000001643127365](figures/zh-cn_image_0000001643127365.png)
430
431
432### 设置图片重复样式
433
434通过objectRepeat属性设置图片的重复样式方式,重复样式请参考[ImageRepeat](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#imagerepeat)枚举说明。
435
436
437```ts
438@Entry
439@Component
440struct MyComponent {
441  build() {
442    Column({ space: 10 }) {
443      Row({ space: 5 }) {
444        Image($r('app.media.ic_public_favor_filled_1'))
445          .width(110)
446          .height(115)
447          .border({ width: 1 })
448          .objectRepeat(ImageRepeat.XY)
449          .objectFit(ImageFit.ScaleDown)
450          // 在水平轴和竖直轴上同时重复绘制图片
451          .overlay('ImageRepeat.XY', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
452        Image($r('app.media.ic_public_favor_filled_1'))
453          .width(110)
454          .height(115)
455          .border({ width: 1 })
456          .objectRepeat(ImageRepeat.Y)
457          .objectFit(ImageFit.ScaleDown)
458          // 只在竖直轴上重复绘制图片
459          .overlay('ImageRepeat.Y', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
460        Image($r('app.media.ic_public_favor_filled_1'))
461          .width(110)
462          .height(115)
463          .border({ width: 1 })
464          .objectRepeat(ImageRepeat.X)
465          .objectFit(ImageFit.ScaleDown)
466          // 只在水平轴上重复绘制图片
467          .overlay('ImageRepeat.X', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
468      }
469    }.height(150).width('100%').padding(8)
470  }
471}
472```
473
474![zh-cn_image_0000001593444112](figures/zh-cn_image_0000001593444112.png)
475
476
477### 设置图片渲染模式
478
479通过renderMode属性设置图片的渲染模式为原色或黑白。
480
481
482```ts
483@Entry
484@Component
485struct MyComponent {
486  build() {
487    Column({ space: 10 }) {
488      Row({ space: 50 }) {
489        Image($r('app.media.example'))
490          // 设置图片的渲染模式为原色
491          .renderMode(ImageRenderMode.Original)
492          .width(100)
493          .height(100)
494          .border({ width: 1 })
495            // overlay是通用属性,用于在组件上显示说明文字
496          .overlay('Original', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
497        Image($r('app.media.example'))
498          // 设置图片的渲染模式为黑白
499          .renderMode(ImageRenderMode.Template)
500          .width(100)
501          .height(100)
502          .border({ width: 1 })
503          .overlay('Template', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
504      }
505    }.height(150).width('100%').padding({ top: 20,right: 10 })
506  }
507}
508```
509
510![zh-cn_image_0000001593293100](figures/zh-cn_image_0000001593293100.png)
511
512
513### 设置图片解码尺寸
514
515通过sourceSize属性设置图片解码尺寸,降低图片的分辨率。
516
517原图尺寸为1280\*960,该示例将图片解码为40\*40和90\*90。
518
519
520```ts
521@Entry
522@Component
523struct Index {
524  build() {
525    Column() {
526      Row({ space: 50 }) {
527        Image($r('app.media.example'))
528          .sourceSize({
529            width: 40,
530            height: 40
531          })
532          .objectFit(ImageFit.ScaleDown)
533          .aspectRatio(1)
534          .width('25%')
535          .border({ width: 1 })
536          .overlay('width:40 height:40', { align: Alignment.Bottom, offset: { x: 0, y: 40 } })
537        Image($r('app.media.example'))
538          .sourceSize({
539            width: 90,
540            height: 90
541          })
542          .objectFit(ImageFit.ScaleDown)
543          .width('25%')
544          .aspectRatio(1)
545          .border({ width: 1 })
546          .overlay('width:90 height:90', { align: Alignment.Bottom, offset: { x: 0, y: 40 } })
547      }.height(150).width('100%').padding(20)
548    }
549  }
550}
551```
552
553![zh-cn_image_0000001593769844](figures/zh-cn_image_0000001593769844.png)
554
555
556### 为图片添加滤镜效果
557
558通过colorFilter修改图片的像素颜色,为图片添加滤镜。
559
560
561```ts
562@Entry
563@Component
564struct Index {
565  build() {
566    Column() {
567      Row() {
568        Image($r('app.media.example'))
569          .width('40%')
570          .margin(10)
571        Image($r('app.media.example'))
572          .width('40%')
573          .colorFilter(
574            [1, 1, 0, 0, 0,
575             0, 1, 0, 0, 0,
576             0, 0, 1, 0, 0,
577             0, 0, 0, 1, 0])
578          .margin(10)
579      }.width('100%')
580      .justifyContent(FlexAlign.Center)
581    }
582  }
583}
584```
585
586![zh-cn_image_0000001643171357](figures/zh-cn_image_0000001643171357.png)
587
588
589### 同步加载图片
590
591一般情况下,图片加载流程会异步进行,以避免阻塞主线程,影响UI交互。但是特定情况下,图片刷新时会出现闪烁,这时可以使用syncLoad属性,使图片同步加载,从而避免出现闪烁。不建议图片加载较长时间时使用,会导致页面无法响应。
592
593
594```ts
595Image($r('app.media.icon'))
596  .syncLoad(true)
597```
598
599
600## 事件调用
601
602通过在Image组件上绑定onComplete事件,图片加载成功后可以获取图片的必要信息。如果图片加载失败,也可以通过绑定onError回调来获得结果。
603
604
605```ts
606@Entry
607@Component
608struct MyComponent {
609  @State widthValue: number = 0
610  @State heightValue: number = 0
611  @State componentWidth: number = 0
612  @State componentHeight: number = 0
613
614  build() {
615    Column() {
616      Row() {
617        Image($r('app.media.ic_img_2'))
618          .width(200)
619          .height(150)
620          .margin(15)
621          .onComplete(msg => {
622            if(msg){
623              this.widthValue = msg.width
624              this.heightValue = msg.height
625              this.componentWidth = msg.componentWidth
626              this.componentHeight = msg.componentHeight
627            }
628          })
629            // 图片获取失败,打印结果
630          .onError(() => {
631            console.info('load image fail')
632          })
633          .overlay('\nwidth: ' + String(this.widthValue) + ', height: ' + String(this.heightValue) + '\ncomponentWidth: ' + String(this.componentWidth) + '\ncomponentHeight: ' + String(this.componentHeight), {
634            align: Alignment.Bottom,
635            offset: { x: 0, y: 60 }
636          })
637      }
638    }
639  }
640}
641```
642
643
644![zh-cn_image_0000001511740460](figures/zh-cn_image_0000001511740460.png)
645