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  62 63 调用方式: 64 65 ``` 66 Image($r('app.media.icon')) 67 ``` 68 69 还可以将图片放在rawfile文件夹下。 70 71 **图2** rawfile 72 73  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 280 281 **图4** 设置绘制颜色后的svg图片 282 283 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 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 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 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 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 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 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 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 651