鸿蒙开发笔记-22-ArkUI:图片(Image), 视频(Video)
HarmonyOS媒体展示开发指南 ArkUI框架为HarmonyOS应用提供Image组件,支持多种图片资源加载方式: 资源类型: 本地资源:通过$r语法访问Resource目录 网络资源:需申请INTERNET权限,支持占位图和错误图设置 媒体库资源:通过file://路径访问 Base64格式:支持网页常用格式 核心功能: 矢量图支持:可渲染SVG格式并修改颜色 缩放模式:提供6种objec
·
在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)
网络资源
- 需申请权限ohos.permission.INTERNET,在
module.json5
中添加:
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
- 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+)
- 对于频繁加载网络图片的场景,官方推荐使用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生态推荐的图片加载库,提供缓存管理、防盗链支持、动图控制等高级功能。
安装与初始化
- 安装依赖:
ohpm install @ohos/imageknife
- 全局初始化(在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组件支持加载本地视频和网络视频。
加载本地视频
普通本地视频
- 加载本地视频时,首先在本地rawfile目录指定对应的文件,
- 再使用资源访问符$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 // 启用硬件解码
})
我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章
更多推荐
所有评论(0)