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  58 59 调用方式: 60 61 ``` 62 Image($r('app.media.icon')) 63 ``` 64 65 还可以将图片放在rawfile文件夹下。 66 67 **图2** rawfile 68 69  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 274 275 **图4** 设置绘制颜色后的svg图片 276 277 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 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 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 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 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 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 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 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 645