在HarmonyOS应用开发中,媒体展示是核心功能之一。ArkUI框架提供了强大的Image和Video组件,支持多种媒体资源加载与高级渲染特性。

显示图片 (Image)

  • 创建图片api:Image(src: PixelMap | ResourceStr | DrawableDescriptor)
加载图片资源
  • Image支持加载存档图、多媒体像素图两种类型。
存档图类型数据源
  • 分为本地资源、网络资源、媒体库资源和base64
本地资源
  • 本地图片需要放置在项目指定目录,并通过相对路径访问:
资源类型 存放路径 访问方式
普通本地资源 ets/images/ Image('images/pic.jpg')
Resource资源 resources/media/ Image($r('app.media.icon'))
Rawfile资源 resources/rawfile/ Image($rawfile('bg.png'))
  • 推荐使用$r语法加载Resource资源,支持跨包访问和系统主题适配。
Image('images/view.jpg')
.width(200)
网络资源
  1. 需申请权限ohos.permission.INTERNET,在module.json5中添加:
"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "always"
    }
  }
]
  1. Image组件的src参数为网络图片的链接(首次加载网络图片时,需要请求网络资源,非首次加载时,默认从缓存中直接读取图片)
Image('https://example.com/image.jpg')
  .width(300)
  .height(200)
  .placeholder($r('app.media.loading')) // 加载占位图(API 9+)
  .error($r('app.media.fail')) // 加载失败图(API 9+)
  1. 对于频繁加载网络图片的场景,官方推荐使用ImageKnife库替代原生Image组件,提供更优的缓存策略和加载性能。
媒体库file://data/storage
  • 支持file://路径前缀的字符串,用于访问通过选择器提供的图片路径。
  • 示例:
@Entry
@Component
struct PhotoSelectPage {
  @State imgDatas: string[] = [];
  // 获取照片url集
  getAllImg() {
    try {
      let PhotoSelectOptions:photoAccessHelper.PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
      PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
      PhotoSelectOptions.maxSelectNumber = 5;
      let photoPicker:photoAccessHelper.PhotoViewPicker = new photoAccessHelper.PhotoViewPicker();
      photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult:photoAccessHelper.PhotoSelectResult) => {
        this.imgDatas = PhotoSelectResult.photoUris;
        console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult));
      }).catch((err:Error) => {
        let message = (err as BusinessError).message;
        let code = (err as BusinessError).code;
        console.error(`PhotoViewPicker.select failed with. Code: ${code}, message: ${message}`);
      });
    } catch (err) {
      let message = (err as BusinessError).message;
      let code = (err as BusinessError).code;
      console.error(`PhotoViewPicker failed with. Code: ${code}, message: ${message}`);    }
  }

  // aboutToAppear中调用上述函数,获取图库的所有图片url,存在imgDatas中
  async aboutToAppear() {
    this.getAllImg();
  }
  // 使用imgDatas的url加载图片。
  build() {
    Column() {
      Grid() {
        ForEach(this.imgDatas, (item:string) => {
          GridItem() {
            Image(item)
              .width(200)
          }
        }, (item:string):string => JSON.stringify(item))
      }
    }.width('100%').height('100%')
  }
}
base64
  • 路径格式为data:image/[png|jpeg|bmp|webp|heif];base64,[base64 data],其中[base64 data]为Base64字符串数据。
  • Base64格式字符串可用于存储图片的像素数据,在网页上使用较为广泛。
多媒体像素图
  • PixelMap是图片解码后的像素图
// 创建PixelMap状态变量
@State pixelMap: PixelMap | undefined;
// 引用网络权限与媒体库权限
import '@ohos.permission';
import '@ohos.multimedia.media';
// 填写网络图片地址
let imageUrl = 'https://example.com/image.jpg';
// 将网络地址成功返回的数据,编码转码成pixelMap的图片格式
fetch(imageUrl)
  .then(response => response.arrayBuffer())
  .then(buffer => {
    let imageSource = image.createImageSource(buffer);
    imageSource.createPixelMap((err, pixelMap) => {
      if (!err) {
        this.pixelMap = pixelMap;
      }
    });
  });
// 显示图片
if (this.pixelMap) {
  Image(this.pixelMap).height(100).width(100)
}
显示矢量图
  • Image组件可显示矢量图(svg格式的图片),支持的svg标签为:svg、rect、circle、ellipse、path、line、polyline、polygon和animate。
  • svg格式的图片可以使用fillColor属性改变图片的绘制颜色。
Image($r('app.media.cloud'))
  .width(50)
  .fillColor(Color.Blue) // 仅对SVG生效
添加属性
设置图片缩放类型
  • 通过objectFit属性使图片缩放到高度和宽度确定的框内。
  • ImageFit.Contain: 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
  • ImageFit.Cover: 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
  • ImageFit.Auto: 自适应显示
  • ImageFit.Fill: 不保持宽高比进行放大缩小,使得图片充满显示边界
  • ImageFit.ScaleDown: 保持宽高比显示,图片缩小或者保持不变
  • ImageFit.None: 保持原有尺寸显示
  • 示例:
@Entry
@Component
struct ObjectFitPage {
  scroller: Scroller = new Scroller()

  build() {
    Scroll(this.scroller) {
      Column() {
        Row() {
          Image($r('app.media.tutorial_pic11'))
            .width(200)
            .height(150)
            .border({ width: 1 })
              // 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
            .objectFit(ImageFit.Contain)
            .margin(15)
            .overlay('Contain', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
          Image($r('app.media.tutorial_pic11'))
            .width(200)
            .height(150)
            .border({ width: 1 })
              // 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
            .objectFit(ImageFit.Cover)
            .margin(15)
            .overlay('Cover', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
          Image($r('app.media.tutorial_pic11'))
            .width(200)
            .height(150)
            .border({ width: 1 })
              // 自适应显示。
            .objectFit(ImageFit.Auto)
            .margin(15)
            .overlay('Auto', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
        }

        Row() {
          Image($r('app.media.tutorial_pic11'))
            .width(200)
            .height(150)
            .border({ width: 1 })
              // 不保持宽高比进行放大缩小,使得图片充满显示边界。
            .objectFit(ImageFit.Fill)
            .margin(15)
            .overlay('Fill', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
          Image($r('app.media.tutorial_pic11'))
            .width(200)
            .height(150)
            .border({ width: 1 })
              // 保持宽高比显示,图片缩小或者保持不变。
            .objectFit(ImageFit.ScaleDown)
            .margin(15)
            .overlay('ScaleDown', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
          Image($r('app.media.tutorial_pic11'))
            .width(200)
            .height(150)
            .border({ width: 1 })
              // 保持原有尺寸显示。
            .objectFit(ImageFit.None)
            .margin(15)
            .overlay('None', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
        }
      }
    }
  }
}
图片插值
  • 当原图分辨率较低并且放大显示时,图片会模糊出现锯齿。这时可以使用interpolation属性对图片进行插值,使图片显示得更清晰。
 Image($r('app.media.grass'))
  .interpolation(ImageInterpolation.None)
  .interpolation(ImageInterpolation.Low)
  .interpolation(ImageInterpolation.Medium)
  .interpolation(ImageInterpolation.High)
设置图片重复样式
  • 通过objectRepeat属性设置图片的重复样式方式,
  • ImageRepeat.XY: 在水平轴和竖直轴上同时重复绘制图片
  • ImageRepeat.Y: 只在竖直轴上重复绘制图片
  • ImageRepeat.X: 只在水平轴上重复绘制图片
设置图片渲染模式
  • 通过renderMode属性设置图片的渲染模式为原色或黑白。
  • ImageRenderMode.Original: 设置图片的渲染模式为原色
  • ImageRenderMode.Template: 设置图片的渲染模式为黑白
设置图片解码尺寸
  • 通过sourceSize属性设置图片解码尺寸,降低图片的分辨率,降低内存占用。
@Entry
@Component
struct Index {
  build() {
    Column() {
      Row({ space: 50 }) {
        Image($r('app.media.example'))
          .sourceSize({
            width: 40,
            height: 40
          })
          .objectFit(ImageFit.ScaleDown)
          .aspectRatio(1)
          .width('25%')
          .border({ width: 1 })
          .overlay('width:40 height:40', { align: Alignment.Bottom, offset: { x: 0, y: 40 } })
        Image($r('app.media.example'))
          .sourceSize({
            width: 90,
            height: 90
          })
          .objectFit(ImageFit.ScaleDown)
          .width('25%')
          .aspectRatio(1)
          .border({ width: 1 })
          .overlay('width:90 height:90', { align: Alignment.Bottom, offset: { x: 0, y: 40 } })
      }.height(150).width('100%').padding(20)
    }
  }
}
为图片添加滤镜效果
  • 通过colorFilter修改图片的像素颜色,为图片添加滤镜。
Image($r('app.media.landscape'))
  .colorFilter([
    0.8, 0.2, 0.2, 0, 0,  // 红色通道调整
    0.2, 0.8, 0.2, 0, 0,  // 绿色通道调整
    0.2, 0.2, 0.8, 0, 0,  // 蓝色通道调整
    0, 0, 0, 1, 0         // 透明度调整
  ])
同步加载图片
  • 一般情况下,图片加载流程会异步进行,以避免阻塞主线程,影响UI交互。但是特定情况下,图片刷新时会出现闪烁,这时可以使用syncLoad属性,使图片同步加载,从而避免出现闪烁。不建议图片加载较长时间时使用,会导致页面无法响应。
Image($r('app.media.icon'))
  .syncLoad(true)
事件调用
  • 通过在Image组件上绑定onComplete事件,图片加载成功后可以获取图片的必要信息。如果图片加载失败,也可以通过绑定onError回调来获得结果。
@Entry
@Component
struct ImageDemo {
  @State imgWidth: number = 0
  @State imgHeight: number = 0

  build() {
    Column() {
      Image($r('app.media.banner'))
        .width('100%')
        .onComplete((msg) => {
          this.imgWidth = msg.width
          this.imgHeight = msg.height
          console.info(`图片加载完成: ${this.imgWidth}x${this.imgHeight}`)
        })
        .onError(() => {
          console.error('图片加载失败')
        })
      Text(`原始尺寸: ${this.imgWidth}x${this.imgHeight}`)
    }
  }
}

高级图片加载-ImageKnife库

ImageKnife库集成

ImageKnife是HarmonyOS生态推荐的图片加载库,提供缓存管理、防盗链支持、动图控制等高级功能。

安装与初始化
  1. 安装依赖
ohpm install @ohos/imageknife
  1. 全局初始化(在EntryAbility中):
import { ImageKnife } from '@ohos/imageknife'

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) {
    // 初始化ImageKnife
    ImageKnife.with(this.context)
    windowStage.loadContent('pages/Index', (err, data) => {})
  }
}
基础使用
import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/imageknife'

@Entry
@Component
struct ImageKnifeDemo {
  private option: ImageKnifeOption = {
    loadSrc: 'https://example.com/banner.jpg',
    placeholderSrc: $r('app.media.loading'),
    errorholderSrc: $r('app.media.fail'),
    objectFit: ImageFit.Cover
  }

  build() {
    ImageKnifeComponent({ imageKnifeOption: this.option })
      .width(300)
      .height(200)
  }
}
防盗链处理

通过自定义请求头解决图片防盗链问题:

// 配置请求头
this.option.headerOption = [
  { key: 'Referer', value: 'https://www.example.com' },
  { key: 'User-Agent', value: 'HarmonyOS ImageKnife' }
]

性能优化

图片缓存机制

ImageKnife提供三级缓存策略:

// 配置缓存策略
this.option.diskCacheStrategy = new DiskCacheStrategy.RESOURCE() // 只缓存原始资源
this.option.memoryCachePolicy = MemoryCachePolicy.AUTO // 自动内存缓存
列表图片优化

在LazyForEach中使用ImageKnife实现预加载:

LazyForEach(this.dataSource, (item) => {
  ImageKnifeComponent({
    imageKnifeOption: {
      loadSrc: item.url,
      preload: true // 启用预加载
    }
  })
    .width(150)
    .height(100)
})
动态分辨率加载

根据设备性能和网络状况加载不同分辨率图片:

// 简化示例:根据网络类型选择图片质量
if (networkType === 'wifi') {
  this.option.loadSrc = item.highResUrl
} else {
  this.option.loadSrc = item.lowResUrl
}

视频播放 (Video)

创建视频组件
  • Video(value: VideoOptions),VideoOptions对象包含参数src、currentProgressRate、previewUri、controller。
  • src指定视频播放源的路径, currentProgressRate用于设置视频播放倍速,previewUri指定视频未播放时的预览图片路径,controller设置视频控制器,用于自定义控制视频。
加载视频资源
  • Video组件支持加载本地视频和网络视频。
加载本地视频
普通本地视频
  1. 加载本地视频时,首先在本地rawfile目录指定对应的文件,
  2. 再使用资源访问符$rawfile()引用视频资源。
@Component
export struct VideoPlayer{
  private controller:VideoController | undefined;
  private previewUris: Resource = $r ('app.media.preview');
  private innerResource: Resource = $rawfile('videoTest.mp4');
  build(){
    Column() {
      Video({
        src: this.innerResource,
        previewUri: this.previewUris,
        controller: this.controller
      })
    }
  }
}
Data Ability提供的视频
  • Data Ability提供的视频路径带有dataability://前缀,使用时确保对应视频资源存在即可。
@Component
export struct VideoPlayer{
   private controller:VideoController | undefined;
   private previewUris: Resource = $r ('app.media.preview');
   private videoSrc: string = 'dataability://device_id/com.domainname.dataability.videodata/video/10'
   build(){
     Column() {
       Video({
         src: this.videoSrc,
         previewUri: this.previewUris,
         controller: this.controller
       })
   }
 }
}
加载沙箱路径视频
  • 支持file:///data/storage路径前缀的字符串,用于读取应用沙箱路径内的资源,需要保证应用沙箱目录路径下的文件存在并且有可读权限。
@Component
export struct VideoPlayer {
  private controller: VideoController | undefined;
  private videoSrc: string = 'file:///data/storage/el2/base/haps/entry/files/show.mp4'

  build() {
    Column() {
      Video({
        src: this.videoSrc,
        controller: this.controller
      })
    }
  }
}
加载网络视频
@Component
export struct VideoPlayer{
  private controller:VideoController | undefined;
  private previewUris: Resource = $r ('app.media.preview');
  private videoSrc: string= 'https://www.example.com/example.mp4' // 使用时请替换为实际视频加载网址
  build(){
    Column() {
      Video({
        src: this.videoSrc,
        previewUri: this.previewUris,
       controller: this.controller
      })
    }
  }
}
常用属性配置
属性 类型 描述
muted boolean 是否静音播放
autoPlay boolean 是否自动播放
controls boolean 是否显示系统控制栏
loop boolean 是否循环播放
objectFit ImageFit 视频缩放方式
currentProgressRate PlaybackSpeed 播放速度(0.75x-2.0x)
  • 示例
@Component
export struct VideoPlayer {
  private controller: VideoController | undefined;

  build() {
    Column() {
      Video({
        controller: this.controller
      })
        .muted(false) //设置是否静音
        .controls(false) //设置是否显示默认控制条
        .autoPlay(false) //设置是否自动播放
        .loop(false) //设置是否循环播放
        .objectFit(ImageFit.Contain) //设置视频适配模式
    }
  }
}
事件调用
  • Video组件回调事件主要为播放开始、暂停结束、播放失败、播放停止、视频准备和操作进度条等事件,除此之外,Video组件也支持通用事件的调用,如点击、触摸等事件的调用。
@Entry
@Component
struct VideoPlayer{
  private controller:VideoController | undefined;
  private previewUris: Resource = $r ('app.media.preview');
  private innerResource: Resource = $rawfile('videoTest.mp4');
  build(){
    Column() {
      Video({
        src: this.innerResource,
        previewUri: this.previewUris,
        controller: this.controller
      })
        .onUpdate((event) => {   //更新事件回调
          console.info("Video update.");
        })
        .onPrepared((event) => {  //准备事件回调
          console.info("Video prepared.");
        })
        .onError(() => {          //失败事件回调
          console.info("Video error.");
        })
        .onStop(() => {          //停止事件回调
          console.info("Video stoped.");
        })
    }
  }
}
Video控制器使用
  • Video控制器主要用于控制视频的状态,包括播放、暂停、停止以及设置进度等
默认控制器
  • 默认的控制器支持视频的开始、暂停、进度调整、全屏显示四项基本功能。
@Entry
@Component
struct VideoGuide {
  @State videoSrc: Resource = $rawfile('videoTest.mp4')
  @State previewUri: string = 'common/videoIcon.png'
  @State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X
  build() {
    Row() {
      Column() {
        Video({
          src: this.videoSrc,
          previewUri: this.previewUri,
          currentProgressRate: this.curRate
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}
自定义控制器
  • 使用自定义的控制器,先将默认控制器关闭掉,之后可以使用button以及slider等组件进行自定义的控制与显示,适合自定义较强的场景下使用。
@Entry
@Component
struct VideoGuide1 {
  @State videoSrc: Resource = $rawfile('videoTest.mp4')
  @State previewUri: string = 'common/videoIcon.png'
  @State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X
  @State isAutoPlay: boolean = false
  @State showControls: boolean = true
  @State sliderStartTime: string = '';
  @State currentTime: number = 0;
  @State durationTime: number = 0;
  @State durationStringTime: string ='';
  controller: VideoController = new VideoController()

  build() {
    Row() {
      Column() {
        Video({
          src: this.videoSrc,
          previewUri: this.previewUri,
          currentProgressRate: this.curRate,
          controller: this.controller
        }).controls(false).autoPlay(true)
        .onPrepared((event)=>{
          if(event){
            this.durationTime = event.duration
          }
        })
        .onUpdate((event)=>{
          if(event){
            this.currentTime =event.time
          }
        })
        Row() {
          Text(JSON.stringify(this.currentTime) + 's')
          Slider({
            value: this.currentTime,
            min: 0,
            max: this.durationTime
          })
          .onChange((value: number, mode: SliderChangeMode) => {
              this.controller.setCurrentTime(value);
            }).width("90%")
          Text(JSON.stringify(this.durationTime) + 's')
        }
        .opacity(0.8)
        .width("100%")
      }
      .width('100%')
    }
    .height('40%')
  }
}
全屏播放处理

原生VideoController的requestFullscreen()方法在部分场景下可能导致自定义控制器失效,推荐通过调整组件尺寸实现全屏效果:

// 替代原生全屏方法
private toggleFullScreen() {
  this.isFullScreen = !this.isFullScreen
  if (this.isFullScreen) {
    // 进入全屏:设置组件占满屏幕
    this.videoHeight = '100%'
    // 隐藏状态栏和导航栏
    window.getLastWindow().setWindowSystemBarProperties({
      statusBarHidden: true,
      navigationBarHidden: true
    })
  } else {
    // 退出全屏:恢复原始尺寸
    this.videoHeight = 250
    // 显示状态栏和导航栏
    window.getLastWindow().setWindowSystemBarProperties({
      statusBarHidden: false,
      navigationBarHidden: false
    })
  }
}
缓冲区设置

通过AVPlayer的preferredBufferDuration属性调整缓冲策略:

import media from '@ohos.multimedia.media'

// 创建带缓冲配置的AVPlayer
createOptimizedPlayer() {
  const player = media.createAVPlayer()
  player.configure({
    preferredBufferDuration: 15 // 缓冲区大小(秒),默认15秒
  })
  return player
}
动态码率切换

对于HLS/DASH流媒体,根据网络状况动态调整码率:

// 监听网络变化
network.on('netAvailable', (data) => {
  if (data.netInfo.signal < -80) { // 弱网络
    this.player.setBitrate(500000) // 切换到500kbps低码率
  } else {
    this.player.setBitrate(2000000) // 恢复2mbps高码率
  }
})
硬件加速解码

启用硬件解码提升播放性能:

player.configure({
  hardwareDecode: true // 启用硬件解码
})

我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐