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