目录

1 简介

2. 使用Picker选择媒体库资源

2.1 指定URI读取文件数据 

2.2 指定URI获取图片或视频资源

3 保存媒体库资源

3.1 使用安全控件保存媒体库资源

3.2 使用弹窗授权保存媒体库资源

 4 使用PhotoPicker组件访问图片/视频

4.1 开发步骤

4.2 完整示例

 5 使用AlbumPicker组件访问相册列表

5.1 开发步骤

6 使用RecentPhoto组件获取最近一张图片

6.1 开发步骤

7 使用PhotoPicker推荐图片

7.1 约束与限制

7.2 开发方式

7.2.1 前提条件

7.2.2 根据特定类型推荐图片

7.2.3 根据文本信息推荐图片

8 使用PickerController将编辑后的图片替换原图 

8.1 替换PhotoPicker中显示的图片/视频

8.1.1 开发步骤

8.1.2 完整示例

8.2 将Picker上替换显示的图片/视频保存到图库

8.2.1 开发步骤

8.2.2 完整示例

9 动态照片

9.1 访问和管理动态照片资源

9.1.1 保存动态照片资源

1、开发步骤

9.1.2 获取动态照片对象

9.1.3 获取媒体库动态照片对象

9.1.4 获取应用沙箱动态照片对象

9.1.5 读取动态照片资源

9.2 使用MovingPhotoView播放动态照片

9.2.1 约束与限制

9.2.2 开发步骤

9.2.3 效果展示


1 简介

Media Library Kit(媒体文件管理服务)提供了管理相册和媒体文件的能力,包括照片和视频,帮助应用快速构建图片视频展示和播放能力。

2. 使用Picker选择媒体库资源

用户有时需要分享图片、视频等用户文件,开发者可以通过特定接口拉起系统图库,用户自行选择待分享的资源,然后最终完成分享。此接口本身无需申请权限,目前适用于界面UIAbility,使用窗口组件触发。具体使用方式如下:

1. 导入选择器模块和文件管理模块。

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';

2. 创建图片-音频类型文件选择选项实例。

const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();

3. 配置可选的媒体文件类型和媒体文件的最大数目

photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE。
photoSelectOptions.maxSelectNumber = 5; // 选择媒体文件的最大数目。

4. 创建图库选择器实例,调用PhotoViewPicker.select接口拉起图库界面进行文件选择。文件选择成功后,返回PhotoSelectResult结果集。

let uris: Array<string> = [];
const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
  uris = photoSelectResult.photoUris;
  console.info('photoViewPicker.select to file succeed and uris are:' + uris);
}).catch((err: BusinessError) => {
  console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
})
  • select返回的uri权限是只读权限,可以根据结果集中uri进行读取文件数据操作。注意不能在picker的回调里直接使用此uri进行打开文件操作,需要定义一个全局变量保存uri,类似使用一个按钮去触发打开文件。可参考指定URI读取文件数据
  • 也可以通过返回的uri获取图片或视频资源
  • 如有获取元数据需求,可以通过文件管理接口文件URI根据uri获取部分文件属性信息,比如文件大小、访问时间、修改时间、文件名、文件路径等。

2.1 指定URI读取文件数据 

1. 待界面从图库返回后,再通过一个类似按钮的组件去调用其他函数,使用fileIo.openSync接口,通过媒体文件uri打开这个文件得到fd。这里需要注意接口权限参数是fileIo.OpenMode.READ_ONLY

let uri: string = '';
let file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd);

2. 通过fd使用fileIo.readSync接口读取这个文件内的数据,读取完成后关闭fd。

let buffer = new ArrayBuffer(4096);
let readLen = fileIo.readSync(file.fd, buffer);
console.info('readSync data to file succeed and buffer size is:' + readLen);
fileIo.closeSync(file);

2.2 指定URI获取图片或视频资源

媒体库支持Picker选择媒体文件URI后,根据指定URI获取图片或视频资源,下面以查询指定URI为'file://media/Photo/1/IMG_datetime_0001/displayName.jpg'为例。

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';

const context = getContext(this);
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

class MediaDataHandler implements photoAccessHelper.MediaAssetDataHandler<ArrayBuffer> {
  onDataPrepared(data: ArrayBuffer) {
    if (data === undefined) {
      console.error('Error occurred when preparing data');
      return;
    }
    console.info('on image data prepared');
    // 应用自定义对资源数据的处理逻辑。
  }
}

async function example() {
  let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
  let uri = 'file://media/Photo/1/IMG_datetime_0001/displayName.jpg' // 需保证此uri已存在。
  predicates.equalTo(photoAccessHelper.PhotoKeys.URI, uri.toString());
  let fetchOptions: photoAccessHelper.FetchOptions = {
    fetchColumns: [photoAccessHelper.PhotoKeys.TITLE],
    predicates: predicates
  };

  try {
    let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOptions);
    let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
    console.info('getAssets photoAsset.uri : ' + photoAsset.uri);
    // 获取属性值,以标题为例;对于非默认查询的属性,get前需要在fetchColumns中添加对应列名。
    console.info('title : ' + photoAsset.get(photoAccessHelper.PhotoKeys.TITLE));
    // 请求图片资源数据。
    let requestOptions: photoAccessHelper.RequestOptions = {
      deliveryMode: photoAccessHelper.DeliveryMode.HIGH_QUALITY_MODE,
    }
    await photoAccessHelper.MediaAssetManager.requestImageData(context, photoAsset, requestOptions, new MediaDataHandler());
    console.info('requestImageData successfully');
    fetchResult.close();
  } catch (err) {
    console.error('getAssets failed with err: ' + err);
  }
}

3 保存媒体库资源

当用户需要保存图片、视频等用户文件到图库时,无需在应用中申请相册管理模块权限'ohos.permission.WRITE_IMAGEVIDEO',应用可以通过安全控件授权弹窗的方式,将用户指定的媒体资源保存到图库中。

3.1 使用安全控件保存媒体库资源

下面以使用安全控件创建一张图片资源为例。

开发步骤

  1. 设置安全控件按钮属性。
  2. 创建安全控件按钮。
  3. 调用MediaAssetChangeRequest.createImageAssetRequestPhotoAccessHelper.applyChanges接口创建图片资源。
import { photoAccessHelper } from '@kit.MediaLibraryKit';

@Entry
@Component
struct Index {
    saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } // 设置安全控件按钮属性。

  build() {
    Row() {
      Column() {
        SaveButton(this.saveButtonOptions) // 创建安全控件按钮。
          .onClick(async (event, result: SaveButtonOnClickResult) => {
             if (result == SaveButtonOnClickResult.SUCCESS) {
               try {
                 let context = getContext();
                 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
                 // 需要确保fileUri对应的资源存在。
                 let fileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/test.jpg';
                 let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, fileUri);
                 await phAccessHelper.applyChanges(assetChangeRequest);
                 console.info('createAsset successfully, uri: ' + assetChangeRequest.getAsset().uri);
               } catch (err) {
                 console.error(`create asset failed with error: ${err.code}, ${err.message}`);
               }
             } else {
               console.error('SaveButtonOnClickResult create asset failed');
             }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

3.2 使用弹窗授权保存媒体库资源

下面以弹窗授权的方式保存一张图片资源为例。

开发步骤

  1. 指定待保存到媒体库的位于应用沙箱的应用文件图片uri。
  2. 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选。
  3. 调用showAssetsCreationDialog,基于弹窗授权的方式获取的目标媒体文件uri。
  4. 将来源于应用沙箱的照片内容写入媒体库的目标uri。
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';

let context = getContext(this);
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

async function example() {
  try {
    // 指定待保存到媒体库的位于应用沙箱的图片uri。
    let srcFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/test.jpg';
    let srcFileUris: Array<string> = [
      srcFileUri
    ];
    // 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选。
    let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
      {
        title: 'test', // 可选。
        fileNameExtension: 'jpg',
        photoType: photoAccessHelper.PhotoType.IMAGE,
        subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选。
      }
    ];
    // 基于弹窗授权的方式获取媒体库的目标uri。
    let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
    // 将来源于应用沙箱的照片内容写入媒体库的目标uri。
    let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);
    let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY);
    await fileIo.copyFile(srcFile.fd, desFile.fd);
    fileIo.closeSync(srcFile);
    fileIo.closeSync(desFile);
    console.info('create asset by dialog successfully');
  } catch (err) {
    console.error(`failed to create asset by dialog successfully errCode is: ${err.code}, ${err.message}`);
  }
}

 4 使用PhotoPicker组件访问图片/视频

当应用需要读取用户图片时,开发者可以在应用界面中嵌入PhotoPicker组件,在用户选择所需要的图片资源后,直接返回该图片资源,而不需要授予应用读取图片文件的权限,即可完成图片或视频文件的访问和读取。

界面效果如图所示。

宫格页

大图页

4.1 开发步骤

1. 导入PhotoPicker模块文件。

import {
  PhotoPickerComponent,
  PickerController,
  PickerOptions,
  DataType,
  BaseItemInfo,
  ItemInfo,
  PhotoBrowserInfo,
  ItemType,
  ClickType,
  MaxCountType,
  PhotoBrowserRange,
  PhotoBrowserUIElement,
  ReminderMode,
  ItemsDeletedCallback,
  ExceedMaxSelectedCallback,
  CurrentAlbumDeletedCallback
} from '@ohos.file.PhotoPickerComponent';
import photoAccessHelper from '@ohos.file.photoAccessHelper';

 2. 创建Picker组件配置选项实例(PickerOptions)和控制实例(PickerController)。

通过PickerOptions,开发者可配置Picker宫格的样式(如勾选框背景色、文本颜色等)、滑动预览方向、最大选择数量等参数,详见PickerOptions API参考

通过PickerController,应用可向Picker组件发送数据。

  // 组件初始化时设置参数信息
  pickerOptions: PickerOptions = new PickerOptions();

  // 组件初始化完成后,可控制组件部分行为
  @State pickerController: PickerController = new PickerController();

  // 已选择的图片
  @State selectUris: Array<string> = new Array<string>();

  //目前选择的图片
  @State currentUri: string = '';
  
  //是否显示大图
  @State isBrowserShow: boolean = false;

3. 应用界面出现时,初始化组件配置选项实例(PickerOptions)。

// 设置picker宫格页数据类型
this.pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE // 图片和照片都显示;
// 最大选择数量
this.pickerOptions.maxSelectNumber = 5;
// 超出最大选择数量时
this.pickerOptions.maxSelectedReminderMode = ReminderMode.TOAST;
// 是否展示搜索框,默认false
this.pickerOptions.isSearchSupported = true;
// 是否支持拍照,默认false
this.pickerOptions.isPhotoTakingSupported = true;

4. 实现回调函数。

通过实现以下回调事件,可在用户在界面操作时,将相关信息报给应用进行处理。

  • 进退大图、切换大图回调,上报的大图相关信息详见PhotoBrowserInfo

说明

  • 回调返回的所有URI均为只读URI,开发者可以根据结果集中的URI读取文件数据。但不能在Picker的回调中直接使用此URI打开文档,需要定义一个全局变量保存URI,样例可参考指定URI读取文件数据指定URI获取图片或视频资源
  • 如需获取元数据,可通过文件管理文件URI接口,根据uri获取部分文件属性信息,比如文件大小、访问时间、修改时间、文件名、文件路径等。
  • 点击图片(缩略图item),将上报图片/视频信息ItemInfo;点击相机item,可拉起系统相机或应用自行处理。
// 资源被选中回调,返回资源的信息,以及选中方式
private onItemClicked(itemInfo: ItemInfo, clickType: ClickType): boolean {
  if (!itemInfo) {
    return false;
  }
  let type: ItemType | undefined = itemInfo.itemType;
  let uri: string | undefined = itemInfo.uri;
  if (type === ItemType.CAMERA) {
    // 点击相机item
    return true; // 返回true则拉起系统相机,若应用需要自行处理则返回false。
  } else {
    if (clickType === ClickType.SELECTED) {
      // 应用做自己的业务处理
      if (uri) {
        this.selectUris.push(uri);
        this.pickerOptions.preselectedUris = [...this.selectUris];
      }
      return true; // 返回true则勾选,否则则不响应勾选。
    } else {
      if (uri) {
        this.selectUris = this.selectUris.filter((item: string) => {
          return item != uri;
        });
        this.pickerOptions.preselectedUris = [...this.selectUris];
      }
    }
    return true;
  }
}

// 进入大图的回调
private onEnterPhotoBrowser(photoBrowserInfo: PhotoBrowserInfo): boolean {
  this.isBrowserShow = true;
  return true;
}

// 退出大图的回调
private onExitPhotoBrowser(photoBrowserInfo: PhotoBrowserInfo): boolean {
  this.isBrowserShow = false;
  return true;
}

// 接收到该回调后,便可通过pickerController相关接口向picker发送数据,在此之前不生效。
private onPickerControllerReady(): void {
  let elements: number[] = [PhotoBrowserUIElement.BACK_BUTTON];
  this.pickerController.setPhotoBrowserUIElementVisibility(elements, false); // 设置大图页不显示返回按钮
}

// 大图左右滑动的回调
private onPhotoBrowserChanged(browserItemInfo: BaseItemInfo): boolean {
  this.currentUri = browserItemInfo.uri ?? '';
  return true;
}

// 已勾选图片被删除时的回调
private onSelectedItemsDeleted(baseItemInfos: Array<BaseItemInfo>): void {
}

// 超过最大选择数量再次点击时的回调
private onExceedMaxSelected(exceedMaxCountType: MaxCountType): void {
}

// 当前相册被删除时的回调
private onCurrentAlbumDeleted(): void {
}

5. 创建PhotoPickerComponent组件。

  PhotoPickerComponent({
    pickerOptions: this.pickerOptions,
    onItemClicked: (itemInfo: ItemInfo, clickType: ClickType): boolean => this.onItemClicked(itemInfo, clickType),
    onEnterPhotoBrowser: (photoBrowserInfo: PhotoBrowserInfo): boolean => this.onEnterPhotoBrowser(photoBrowserInfo),
    onExitPhotoBrowser: (photoBrowserInfo: PhotoBrowserInfo): boolean => this.onExitPhotoBrowser(photoBrowserInfo),
    onPickerControllerReady: (): void => this.onPickerControllerReady(),
    onPhotoBrowserChanged: (browserItemInfo: BaseItemInfo): boolean => this.onPhotoBrowserChanged(browserItemInfo),
    onSelectedItemsDeleted: (BaseItemInfo: Array<BaseItemInfo>) => this.onSelectedItemsDeleted(BaseItemInfo),
    onExceedMaxSelected: (exceedMaxCountType: MaxCountType) => this.onExceedMaxSelected(exceedMaxCountType),
    onCurrentAlbumDeleted: () => this.onCurrentAlbumDeleted(),
    pickerController: this.pickerController,
  })

 6. 通过PickerControllerPicker组件发送数据,实现控制PhotoPickerComponent组件行为。

4.2 完整示例

import {
  PhotoPickerComponent,
  PickerController,
  PickerOptions,
  DataType,
  BaseItemInfo,
  ItemInfo,
  PhotoBrowserInfo,
  ItemType,
  ClickType,
  MaxCountType,
  PhotoBrowserRange,
  PhotoBrowserUIElement,
  ReminderMode,
  ItemsDeletedCallback,
  ExceedMaxSelectedCallback,
  CurrentAlbumDeletedCallback
} from '@ohos.file.PhotoPickerComponent';
import photoAccessHelper from '@ohos.file.photoAccessHelper';

@Entry
@Component
struct PhotoPickerComponentDemo {
  // 组件初始化时设置参数信息
  pickerOptions: PickerOptions = new PickerOptions();

  // 组件初始化完成后,可控制组件部分行为
  @State pickerController: PickerController = new PickerController();

  // 已选择的图片
  @State selectUris: Array<string> = new Array<string>();

  //目前选择的图片
  @State currentUri: string = '';

  //是否显示大图
  @State isBrowserShow: boolean = false;

  aboutToAppear() {
    // 设置picker宫格页数据类型
    this.pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE // 图片和照片都显示;
    // 最大选择数量
    this.pickerOptions.maxSelectNumber = 5;
    // 超出最大选择数量时
    this.pickerOptions.maxSelectedReminderMode = ReminderMode.TOAST;
    // 是否展示搜索框,默认false
    this.pickerOptions.isSearchSupported = true;
    // 是否支持拍照,默认false
    this.pickerOptions.isPhotoTakingSupported = true;

  }

  // 资源被选中回调,返回资源的信息,以及选中方式
  private onItemClicked(itemInfo: ItemInfo, clickType: ClickType): boolean {
    if (!itemInfo) {
      return false;
    }
    let type: ItemType | undefined = itemInfo.itemType;
    let uri: string | undefined = itemInfo.uri;
    if (type === ItemType.CAMERA) {
      // 点击相机item
      return true; // 返回true则拉起系统相机,若应用需要自行处理则返回false。
    } else {
      if (clickType === ClickType.SELECTED) {
        // 应用做自己的业务处理
        if (uri) {
          this.selectUris.push(uri);
          this.pickerOptions.preselectedUris = [...this.selectUris];
        }
        return true; // 返回true则勾选,否则则不响应勾选。
      } else {
        if (uri) {
          this.selectUris = this.selectUris.filter((item: string) => {
            return item != uri;
          });
          this.pickerOptions.preselectedUris = [...this.selectUris];
        }
      }
      return true;
    }
  }

  // 进入大图的回调
  private onEnterPhotoBrowser(photoBrowserInfo: PhotoBrowserInfo): boolean {
    this.isBrowserShow = true;
    return true;
  }

  // 退出大图的回调
  private onExitPhotoBrowser(photoBrowserInfo: PhotoBrowserInfo): boolean {
    this.isBrowserShow = false;
    return true;
  }

  // 接收到该回调后,便可通过pickerController相关接口向picker发送数据,在此之前不生效。
  private onPickerControllerReady(): void {
  }

  // 大图左右滑动的回调
  private onPhotoBrowserChanged(browserItemInfo: BaseItemInfo): boolean {
    this.currentUri = browserItemInfo.uri ?? '';
    return true;
  }

  // 已勾选图片被删除时的回调
  private onSelectedItemsDeleted(baseItemInfos: Array<BaseItemInfo>): void {
  }

  // 超过最大选择数量再次点击时的回调
  private onExceedMaxSelected(exceedMaxCountType: MaxCountType): void {
  }

  // 当前相册被删除时的回调
  private onCurrentAlbumDeleted(): void {
  }

  build() {
    Flex({
      direction: FlexDirection.Column,
      alignItems: ItemAlign.Start
    }) {
      PhotoPickerComponent({
        pickerOptions: this.pickerOptions,
        onItemClicked: (itemInfo: ItemInfo, clickType: ClickType): boolean => this.onItemClicked(itemInfo, clickType),
        onEnterPhotoBrowser: (photoBrowserInfo: PhotoBrowserInfo): boolean => this.onEnterPhotoBrowser(photoBrowserInfo),
        onExitPhotoBrowser: (photoBrowserInfo: PhotoBrowserInfo): boolean => this.onExitPhotoBrowser(photoBrowserInfo),
        onPickerControllerReady: (): void => this.onPickerControllerReady(),
        onPhotoBrowserChanged: (browserItemInfo: BaseItemInfo): boolean => this.onPhotoBrowserChanged(browserItemInfo),
        onSelectedItemsDeleted: (BaseItemInfo: Array<BaseItemInfo>) => this.onSelectedItemsDeleted(BaseItemInfo),
        onExceedMaxSelected: (exceedMaxCountType: MaxCountType) => this.onExceedMaxSelected(exceedMaxCountType),
        onCurrentAlbumDeleted: () => this.onCurrentAlbumDeleted(),
        pickerController: this.pickerController,
      })

      // 这里模拟应用侧底部的选择栏
      if (this.isBrowserShow) {
        //已选择的图片缩影图
        Row() {
          ForEach(this.selectUris, (uri: string) => {
            if (uri === this.currentUri) {
              Image(uri).height(50).width(50)
                .onClick(() => {
                })
                .borderWidth(1)
                .borderColor('red')
            } else {
              Image(uri).height(50).width(50).onClick(() => {
                this.pickerController.setData(DataType.SET_SELECTED_URIS, this.selectUris);
                this.pickerController.setPhotoBrowserItem(uri, PhotoBrowserRange.ALL);
              })
            }
          }, (uri: string) => JSON.stringify(uri))
        }.alignSelf(ItemAlign.Center).margin(this.selectUris.length ? 10 : 0)
      } else {
        // 进入大图,预览已选择的图片
        Button('预览').width('33%').alignSelf(ItemAlign.Start).height('5%').margin(10).onClick(() => {
          if (this.selectUris.length > 0) {
            this.pickerController.setPhotoBrowserItem(this.selectUris[0], PhotoBrowserRange.SELECTED_ONLY);
          }
        })
      }
    }
  }
}

 5 使用AlbumPicker组件访问相册列表

可以在布局中嵌入AlbumPickerComponent组件,通过此组件,应用无需申请权限,即可访问公共目录中的相册列表

需配合PhotoPickerComponent一起使用,用户通过AlbumPickerComponent组件选择对应相册并通知PhotoPickerComponent组件刷新成对应相册的图片和视频

界面效果如图所示。

5.1 开发步骤

1. 导入相册组件模块文件。

import { AlbumPickerComponent, AlbumPickerOptions, AlbumInfo} from '@kit.MediaLibraryKit';

2. 创建相册组件配置选项实例(AlbumPickerOptions)。

// 用于相册组件初始化时设置参数信息
albumOptions: AlbumPickerOptions = new AlbumPickerOptions();

3. 初始化组件配置选项实例(AlbumPickerOptions)。

/**
 * 设置相册页颜色模式, 默认AUTO。
 * AUTO:跟随系统的模式, LIGHT:浅色模式, DARK:深色模式
 */
this.albumOptions.themeColorMode = PickerColorMode.AUTO;

4. 创建AlbumPickerComponent组件。

AlbumPickerComponent({
  // 设置组件选择选项实例
  albumPickerOptions: this.albumOptions,

  /**
   *相册被选中回调,返回相册信息
   * AlbumInfo(uri)
   */
  onAlbumClick: (albumInfo: AlbumInfo): boolean => this.onAlbumClick(albumInfo),
})

5. 与PhotoPicker组件联动,将相册URI给到应用,根据相册URI更新PhotoPicker组件宫格页内容。

private onAlbumClick(albumInfo: AlbumInfo): boolean {
    if (albumInfo?.uri) {
        // 根据相册url更新宫格页内容
        this.pickerController.setData(DataType.SET_ALBUM_URI, albumInfo.uri);      
    }
    return true;
}

6 使用RecentPhoto组件获取最近一张图片

应用可以在布局中嵌入最近图片组件,通过此组件,应用无需申请权限,即可指定配置访问公共目录中最近的一个图片或视频文件。授予的权限仅包含只读权限

界面效果如图所示。

①为PhotoPicker宫格页,②为RecentPhoto最近图片的效果。

6.1 开发步骤

1. 导入最近图片组件模块文件。

import { BaseItemInfo } from '@ohos.file.PhotoPickerComponent';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
import {
  PhotoSource,
  RecentPhotoCheckResultCallback,
  RecentPhotoClickCallback,
  RecentPhotoComponent,
  RecentPhotoOptions
} from '@ohos.file.RecentPhotoComponent';

2. 创建最近图片组件选择选项实例(RecentPhotoOptions)。

// 最近图片组件初始化
recentPhotoOptions: RecentPhotoOptions = new RecentPhotoOptions();

3. 初始化最近图片组件选择选项实例(RecentPhotoOptions)。

// 设置数据类型, IMAGE_VIDEO_TYPE:图片和视频(默认值)  IMAGE_TYPE:图片   VIDEO_TYPE:视频  MOVING_PHOTO_IMAGE_TYPE 动态图片
this.recentPhotoOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE;

// 设置最近图片的时间范围,单位(秒), 0表示所有时间。
this.recentPhotoOptions.period = 0;

// 设置资源的来源 ALL:所有 CAMERA:相机  SCREENSHOT:截图
this.recentPhotoOptions.photoSource = PhotoSource.ALL;

4. 创建RecentPhotoComponent组件。

RecentPhotoComponent({
    // 设置最近图片组件选择选项实例
    recentPhotoOptions: this.recentPhotoOptions,
    
     /**
     * 最近图片点击事件
     * BaseItemInfo(uri, mimeType, width, height, size, duration)
     * return 返回值为true时才会给url授权,才可以显示
     */
    onRecentPhotoClick: (recentPhotoInfo: BaseItemInfo): boolean => this.onRecentPhotoClick(recentPhotoInfo),

    // 检查是否存在最近的资源
    onRecentPhotoCheckResult: (recentPhotoExists: boolean) => this.onReceiveCheckResult(recentPhotoExists),
})

5. 实现相关回调。

实现onReceiveCheckResult回调,可查询是否存在最近图片,仅返回true时才可进一步实现控制是否显示最近图片。

实现onRecentPhotoClick回调,将上报返回图片/视频相关信息BaseItemInfo

// 返回值为true时,才能获取uri的权限
private onRecentPhotoClick(recentPhotoInfo: BaseItemInfo): boolean {
    if (!recentPhotoInfo) {
      return false;
    }
    return true;
  }

private onReceiveCheckResult(recentPhotoExists: boolean): void {
    if (!recentPhotoExists) {
      console.info('not exist recent photo');
    }
    // 存在最近图片的话,可以实现业务需求, 如去控制RecentPhotoComponent是否显示
 }

7 使用PhotoPicker推荐图片

应用在调用PhotoPicker接口时,如果配置了PhotoPicker图片推荐参数,当设备中有满足图片推荐参数的图片,且设备中的图片已经分析完成时,PhotoPicker界面除了展示全量的图片外,还会展示符合条件的推荐图片供用户参考选择,从而缩短用户筛选图片的时间。

  • 选择特定类型(RecommendationType)的图片,如身份证、银行卡、驾驶证、行驶证、二维码等。当有符合应用指定类型的图片传入时,将显示对应的图片推荐页。
  • 以指定图片类型为二维码为例,PhotoPicker界面上将出现“二维码”的Tab页,展示图库中的二维码图片

  • 在图文编辑时选择图片,系统可以根据应用传入的文本信息,提取出时间、地点、事物或活动,并根据提取的信息,推荐对应的图片展示在PhotoPicker中。
  • 举例说明,如设置的推荐参数文本是“国庆节,带着女儿去了上海野生动物园,看到了凶猛的大象,漂亮的火烈鸟,还有她心心念念的大熊猫,小家伙可开心了。”
  • 而且手机中有相应的图片,图片分析完成时,会在“推荐”的Tab页中展示出时间是国庆节,地点是上海野生动物园的大熊猫、火烈鸟、大象的图片。

7.1 约束与限制

  • PhotoPicker推荐图片的功能依赖于图片分析的结果,只有在媒体库中的图片分析完成后,系统才会进行推荐。

    当设备在灭屏充电时,在电量充足、温度正常的情况下,系统将自动进行图片分析。

    如果图片带有地理位置信息,设备需要联网才可以正常解析图片中的位置信息。

  • 当前仅支持推荐图片类型的文件。

7.2 开发方式

7.2.1 前提条件

开发者可以通过两种方式实现PhotoPicker,拉起系统图库。请选择其中一种方式,实现PhotoPicker。

说明

  • 当MIMEType配置为IMAGE_TYPE或IMAGE_VIDEO_TYPE,并在后续步骤中配置了推荐参数,将根据参数推荐图片类型的文件。页签“全部”下展示的文件类型与MIMEType配置项一致。
  • 当MIMEType配置为其他类型,推荐不生效。正常展示PhotoPicker筛选的全部照片。

7.2.2 根据特定类型推荐图片

当需要推荐特定类型的图片,如身份证、银行卡、驾驶证、行驶证、二维码等,可通过配置RecommendationOptions.recommendationType,指定推荐的图片类型。支持的图片类型可参考RecommendationType

let recommendationOptions: photoAccessHelper.RecommendationOptions = {
  recommendationType: photoAccessHelper.RecommendationType.QR_CODE // 配置枚举类型,不同的枚举值,推荐不同种类的图片
};
this.pickerOptions.recommendationOptions = recommendationOptions;// 将推荐参数赋值给 pickerOptions.recommendationOptions

说明

当前示例以通过Picker组件实现推荐图片为例。

如果使用Picker接口,需要将推荐参数赋值给 photoSelectOptions.recommendationOptions。

7.2.3 根据文本信息推荐图片

当需要在图文编辑时,根据文本信息推荐图片,可通过配置RecommendationOptions.textContextInfo。

说明

如果RecommendationOptions同时配置了recommendationType和textContextInfo,仅textContextInfo生效。

let textInfo: photoAccessHelper.TextContextInfo = {
  text: '国庆节,带着女儿去了上海野生动物园,看到了凶猛的大象,漂亮的火烈鸟,还有她心心念念的大熊猫,小家伙可开心了。'
};
let recommendationOptions: photoAccessHelper.RecommendationOptions = {
  textContextInfo: textInfo
};
this.pickerOptions.recommendationOptions = recommendationOptions;// 将推荐参数赋值给 pickerOptions.recommendationOptions

说明

当前示例以通过Picker组件实现推荐图片为例。

如果使用Picker接口,需要将推荐参数赋值给 photoSelectOptions.recommendationOptions。

8 使用PickerController将编辑后的图片替换原图 

8.1 替换PhotoPicker中显示的图片/视频

应用可获得用户从Picker选择的图片、视频的访问权限,读取图片、视频后进行编辑、修改。完成编辑修改后的图片/视频缓存到应用沙箱后,可调用本API,将编辑结果文件发送给PhotoPicker,并指定替换显示的原图。Picker根据指定将接收的编辑结果文件替换原图片进行显示。

效果如图所示。

8.1.1 开发步骤

1. 导入选择器模块和文件管理模块。

import { PickerController } from '@ohos.file.PhotoPickerComponent';
import { fileUri } from '@kit.CoreFileKit';

2. 创建参数列表。

@State pickerController: PickerController = new PickerController();
@State originUrl: string = ''; // 原图URI
@State replaceUrl: string = ''; // 原图编辑后的沙箱URI

3. 调用replacePhotoPickerPreview()替换图片/视频。

this.pickerController.replacePhotoPickerPreview(this.originUrl, this.replaceUrl, (a, b) => {
  console.log("hello this.pickerController.replaceUrl code res:" + b)
})

8.1.2 完整示例

import { PickerController } from '@ohos.file.PhotoPickerComponent';
import { fileUri } from '@kit.CoreFileKit';

@Entry
@Component
struct Index {
  @State pickerController: PickerController = new PickerController();
  @State originUrl: string = '';
  @State replaceUrl: string = '';

  build() {
    Row() {
    Column() {
      Button("replace url").width('25%').height('50%').margin({ top: 10 }).onClick(() => {
        this.originUrl = 'file://media/photo/test.jpg';
        this.replaceUrl = fileUri.getUriFromPath(getContext().filesDir + "/test.jpg").toString();
        this.pickerController.replacePhotoPickerPreview(this.originUrl, this.replaceUrl, (err, b) => {
          console.log("hello this.pickerController.replaceUrl code res:" + b)
        })
      })
    }
    .width('100%')
    }
    .height('100%')
  }
}

8.2 将Picker上替换显示的图片/视频保存到图库

应用指定保存的文件,需在替换显示的范围内。应用调用API后,PhotoPicker将在Picker上成功替换显示的图片、视频保存到图库。确保保存的内容与替换显示的图片、视频一致。

效果如图所示。

8.2.1 开发步骤

1. 导入选择器模块和文件管理模块。

import photoAccessHelper from '@ohos.file.photoAccessHelper';
import { PickerController, PickerOptions, SaveMode } from '@ohos.file.PhotoPickerComponent';
import { fileUri } from '@kit.CoreFileKit';

2. 创建参数列表。

@State pickerController: PickerController = new PickerController();
@State originUrl: string = ''; // 原图URI
@State replaceUrl: string = ''; // 原图编辑后的沙箱URI

3. 调用saveTrustedPhotoAssets()保存图片/视频到图库。

this.pickerController.(this.replaceUris, (a, b) => {
  console.log("hello this.pickerController.save as new code a.code:" + a.code + ",a.message:" + a.message + ",res:" + b)
}, photoCreationConfigs, SaveMode.SAVE_AS); // SaveMode: SAVE_AS = 0(另存为),OVERWRITE = 1 (覆盖保存)

该接口使用依赖pickerController.replacePhotoPickerPreview,需要先执行pickerController.replacePhotoPickerPreview后才能执行pickerController.saveTrustedPhotoAssets。

8.2.2 完整示例

import photoAccessHelper from '@ohos.file.photoAccessHelper';
import { PickerController, PickerOptions, SaveMode } from '@ohos.file.PhotoPickerComponent';
import { fileUri } from '@kit.CoreFileKit';

@Entry
@Component
struct Index {
  @State pickerController: PickerController = new PickerController();
  @State originUrl: string = '';
  @State replaceUrl: string = '';

  build() {
    Row() {
    Column() {
      Button("replace url").width('25%').height('50%').margin({ top: 10 }).onClick(() => {
        this.originUrl = 'file://media/photo/test.jpg';
        this.replaceUrl = fileUri.getUriFromPath(getContext().filesDir + "/test.jpg").toString();
        this.pickerController.replacePhotoPickerPreview(this.originUrl, this.replaceUrl, (err, b) => {
          console.log("hello this.pickerController.replaceUrl code res:" + b)
        })
        let photoCreationConfig: photoAccessHelper.PhotoCreationConfig = {
          title: 'test',
          fileNameExtension: 'jpg',
          photoType: photoAccessHelper.PhotoType.IMAGE,
          subtype: photoAccessHelper.PhotoSubtype.DEFAULT,
        };
        this.pickerController.saveTrustedPhotoAssets([this.replaceUrl], (err, arr) => {
          console.log("testPicker27 this.pickerController.save code res:" + err.code);
        }, [photoCreationConfig], SaveMode.SAVE_AS);
      })
    }
    .width('100%')
    }
    .height('100%')
  }
}

9 动态照片

9.1 访问和管理动态照片资源

9.1.1 保存动态照片资源

使用安全控件保存动态照片资源后,可用于获取MovingPhoto对象,从而完成播放动态照片等操作。

使用安全控件保存动态照片资源,无需申请相册管理模块权限'ohos.permission.WRITE_IMAGEVIDEO',允许用户通过点击按钮临时获取存储权限,并将资源直接保存到指定的媒体库路径,使得操作更为便捷。

1、开发步骤
  1. 设置安全控件按钮属性。

  2. 创建安全控件按钮。

  3. 调用MediaAssetChangeRequest.createAssetRequest接口新建一个创建资产的变更请求,指定待创建资产的子类型为动态照片。

  4. 调用MediaAssetChangeRequest.addResource接口指定动态照片的图片和视频内容。

    以下示例以从应用沙箱的应用文件fileUri指定动态照片的图片和视频内容为例。

    开发者可根据实际情况,通过ArrayBuffer的方式指定资源内容,参考MediaAssetChangeRequest.addResource(type: ResourceType, data: ArrayBuffer)

  5. 调用PhotoAccessHelper.applyChanges接口提交创建资产的变更请求。

import { photoAccessHelper } from '@kit.MediaLibraryKit';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  @State saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } // 设置安全控件按钮属性。

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        SaveButton(this.saveButtonOptions) // 创建安全控件按钮。
          .onClick(async (event, result: SaveButtonOnClickResult) => {
             if (result == SaveButtonOnClickResult.SUCCESS) {
               try {
                 let context = getContext();
                 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
                 // 需要确保imageFileUri和videoFileUri对应的资源存在,分别表示待创建到媒体库的动态照片的图片和视频。
                 let imageFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/create_moving_photo.jpg';
                 let videoFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/create_moving_photo.mp4';
                 let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context, photoAccessHelper.PhotoType.IMAGE, "jpg", {
                  title: "moving_photo",
                  subtype: photoAccessHelper.PhotoSubtype.MOVING_PHOTO
                 });
                 assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, imageFileUri);
                 assetChangeRequest.addResource(photoAccessHelper.ResourceType.VIDEO_RESOURCE, videoFileUri);
                 await phAccessHelper.applyChanges(assetChangeRequest);
                 console.info('create moving photo successfully, uri: ' + assetChangeRequest.getAsset().uri);
               } catch (err) {
                 console.error(`create moving photo failed with error: ${err.code}, ${err.message}`);
               }
             } else {
               console.error('SaveButtonOnClickResult create moving photo failed');
             }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

9.1.2 获取动态照片对象

  • 应用可以通过Picker的方式获取用户媒体库里的动态照片对象,后续可用于在应用内播放动态照片,或是读取动态照片资源进行其他操作(如上传到应用共享给他人浏览等)。

  • 应用也可以通过传入应用沙箱的应用文件图片和视频fileUri的方式构造应用本地的动态照片对象。

9.1.3 获取媒体库动态照片对象

  1. 通过Picker选择动态照片的媒体文件uri。
  2. 调用PhotoAccessHelper.getAssetsFetchResult.getFirstObject接口获取uri对应的PhotoAsset资产。
  3. 调用MediaAssetManager.requestMovingPhoto获取PhotoAsset对应的动态照片对象(MovingPhoto)。
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';
let context = getContext(this);
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

async function example() {
  try {
    // picker选择动态照片uri。
    let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.MOVING_PHOTO_IMAGE_TYPE;
    photoSelectOptions.maxSelectNumber = 9;
    let photoViewPicker = new photoAccessHelper.PhotoViewPicker();
    let photoSelectResult = await photoViewPicker.select(photoSelectOptions);
    let uris = photoSelectResult.photoUris;
    for (let i = 0; i < uris.length; i++) {
      // 获取uri对应的PhotoAsset资产。
      let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
      predicates.equalTo(photoAccessHelper.PhotoKeys.URI, uris[i]);
      let fetchOption: photoAccessHelper.FetchOptions = {
        fetchColumns: [],
        predicates: predicates
      };
      let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOption);
      let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
      // 获取PhotoAsset对应的动态照片对象。
      await photoAccessHelper.MediaAssetManager.requestMovingPhoto(context, photoAsset, {
        deliveryMode: photoAccessHelper.DeliveryMode.FAST_MODE
      }, {
        async onDataPrepared(movingPhoto: photoAccessHelper.MovingPhoto) {
          if (movingPhoto !== undefined) {
            // 应用可自定义对movingPhoto的处理逻辑。
            console.info('request moving photo successfully, uri: ' + movingPhoto.getUri());
          }
        }
      })
    }
  } catch (err) {
    console.error(`request moving photo failed with error: ${err.code}, ${err.message}`);
  }
}

9.1.4 获取应用沙箱动态照片对象

调用MediaAssetManager.loadMovingPhoto加载应用沙箱的动态照片对象(MovingPhoto)。

import { photoAccessHelper } from '@kit.MediaLibraryKit';
let context = getContext(this);

async function example() {
  try {
    let imageFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/local_moving_photo.jpg';
    let videoFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/local_moving_photo.mp4';
    let movingPhoto = await photoAccessHelper.MediaAssetManager.loadMovingPhoto(context, imageFileUri, videoFileUri);
    console.info('load moving photo successfully');
  } catch (err) {
    console.error(`load moving photo failed with error: ${err.code}, ${err.message}`);
  }
}

9.1.5 读取动态照片资源

对于一个动态照片对象,应用可以通过MovingPhoto.requestContent导出图片和视频到应用沙箱,或者读取图片或视频的ArrayBuffer内容。

import { photoAccessHelper } from '@kit.MediaLibraryKit';

async function example(movingPhoto: photoAccessHelper.MovingPhoto) {
  try {
    let imageFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/request_moving_photo.jpg';
    let videoFileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/request_moving_photo.mp4';
    await movingPhoto.requestContent(imageFileUri, videoFileUri); // 将动态照片导出到应用沙箱。
    let imageData = await movingPhoto.requestContent(photoAccessHelper.ResourceType.IMAGE_RESOURCE); // 读取图片的ArrayBuffer内容。
    let videoData = await movingPhoto.requestContent(photoAccessHelper.ResourceType.VIDEO_RESOURCE); // 读取视频的ArrayBuffer内容。
  } catch (err) {
    console.error(`request content of moving photo failed with error: ${err.code}, ${err.message}`);
  }
}

9.2 使用MovingPhotoView播放动态照片

系统提供了MovingPhotoView组件,在一些社交类、图库类应用中,可用于播放动态照片文件。

9.2.1 约束与限制

针对MovingPhotoView组件的使用,有以下约束与限制:

  • 当前不支持动态属性设置。
  • 当前不支持设置ArkUI通用属性expandSafeArea
  • 该组件长按触发播放时组件区域放大为1.1倍。
  • 该组件使用AVPlayer进行播放,同时开启的AVPlayer个数不建议超过3个,超过3个可能会出现视频播放卡顿现象。

9.2.2 开发步骤

1. 导入动态照片模块。

import { MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit';

2. 获取动态照片对象(MovingPhoto)。

MovingPhoto对象需要通过photoAccessHelper接口创建或获取,MovingPhotoView只接收构造完成的MovingPhoto对象。

src: photoAccessHelper.MovingPhoto | undefined = undefined;

3. 创建动态照片控制器(MovingPhotoViewController),用于控制动态照片的播放状态(如播放、停止)。

controller: MovingPhotoViewController = new MovingPhotoViewController();

4. 创建动态照片组件。

以下参数取值仅为举例,具体每个属性的取值范围,可参考API文档:@ohos.multimedia.movingphotoview

 import { photoAccessHelper, MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit';

 @Entry
 @Component
 struct Index {
   @State src: photoAccessHelper.MovingPhoto | undefined = undefined
   @State isMuted: boolean = false
   controller: MovingPhotoViewController = new MovingPhotoViewController();
   build() {
     Column() {
       MovingPhotoView({
         movingPhoto: this.src,
         controller: this.controller
         // imageAIOptions: this.options.
       })
         // 是否静音播放,此处由按钮控制,默认值为false非静音播放。
         .muted(this.isMuted)
         // 视频显示模式,默认值为Cover。
         .objectFit(ImageFit.Cover)
         // 播放时触发。
         .onStart(() => {
           console.log('onStart');
         })
         // 播放结束触发。
         .onFinish(() => {
           console.log('onFinish');
         })
         // 播放停止触发。
         .onStop(() => {
           console.log('onStop')
         })
         // 出现错误触发。
         .onError(() => {
           console.log('onError');
         })
 
       Row() {
         // 按钮:开始播放。
         Button('start')
           .onClick(() => {
             this.controller.startPlayback()
           })
           .margin(5)
         // 按钮:停止播放。
         Button('stop')
           .onClick(() => {
             this.controller.stopPlayback()
           })
           .margin(5)
         // 按钮:是否静音播放。
         Button('mute')
           .onClick(() => {
             this.isMuted = !this.isMuted
           })
           .margin(5)
       }
       .alignItems(VerticalAlign.Center)
       .justifyContent(FlexAlign.Center)
       .height('15%')
     }
   }
 }

9.2.3 效果展示

 

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐