前言
在使用App的時候掩浙,我們經常會在一些社交軟件中聊天時發(fā)一些圖片或者文件之類的多媒體文件花吟,那在鴻蒙原生應用中,我們怎么開發(fā)這樣的功能呢厨姚? 本文會給大家對這個功能點進行講解衅澈,我們采用的是拉起系統(tǒng)組件來進行圖片、文件的選擇谬墙,拉起系統(tǒng)相機進行拍照的這樣一種實現(xiàn)方式今布。
創(chuàng)建多媒體Demo工程
我們使用Empty 模板創(chuàng)建一個Demo工程。
創(chuàng)建MediaBean 實體類
在src->main->ets 下面創(chuàng)建bean文件夾拭抬,在文件夾下創(chuàng)建MediaBean.ts文件
/**
* 多媒體數(shù)據(jù)類
*/
export class MediaBean {
/**
* 文件名稱
*/
public fileName: string;
/**
* 文件大小
*/
public fileSize: number;
/**
* 文件類型
*/
public fileType: string;
/**
* 本地存儲地址
*/
public localUrl: string;
}
創(chuàng)建MediaHelper工具類
在src->main->ets 下面創(chuàng)建helper 文件夾部默,在文件夾下創(chuàng)建MediaHelper.ts文件
MediaHelper.ts 完整代碼如下:
import common from '@ohos.app.ability.common';
import picker from '@ohos.file.picker';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
import wantConstant from '@ohos.ability.wantConstant';
import { MediaBean } from '../bean/MediaBean';
import { StringUtils } from '../utils/StringUtils';
import { Log } from '../utils/Log';
/**
* 多媒體輔助類
*/
export class MediaHelper {
private readonly TAG: string = 'MediaHelper';
private mContext: common.Context;
constructor(context: common.Context) {
this.mContext = context;
}
/**
* 選擇圖片
*/
public selectPicture(): Promise<MediaBean> {
try {
let photoSelectOptions = new picker.PhotoSelectOptions();
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
let photoPicker = new picker.PhotoViewPicker();
return photoPicker.select(photoSelectOptions)
.then((photoSelectResult) => {
Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));
if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {
let filePath = photoSelectResult.photoUris[0];
Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + filePath);
return filePath;
}
}).catch((err) => {
Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);
return err;
}).then(async (filePath) => {
const mediaBean = await this.buildMediaBean(filePath);
return mediaBean;
});
} catch (err) {
Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);
return Promise.reject(err);
}
}
/**
* 選擇文件
*/
public selectFile(): Promise<MediaBean> {
try {
let documentSelectOptions = new picker.DocumentSelectOptions();
let documentPicker = new picker.DocumentViewPicker();
return documentPicker.select(documentSelectOptions)
.then((documentSelectResult) => {
Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + JSON.stringify(documentSelectResult));
if (documentSelectResult && documentSelectResult.length > 0) {
let filePath = documentSelectResult[0];
Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + filePath);
return filePath;
}
}).catch((err) => {
Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);
return err;
}).then(async (filePath) => {
const mediaBean = await this.buildMediaBean(filePath);
return mediaBean;
});
} catch (err) {
Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);
return Promise.reject(err);
}
}
/**
* 拍照
*/
public async takePhoto(context: common.UIAbilityContext): Promise<MediaBean> {
let want = {
'uri': '',
'action': wantConstant.Action.ACTION_IMAGE_CAPTURE,
'parameters': {},
};
return context.startAbilityForResult(want)
.then((result) => {
Log.info(this.TAG, `startAbility call back , ${JSON.stringify(result)}`);
if (result.resultCode === 0 && result.want && StringUtils.isNotNullOrEmpty(result.want.uri)) {
//拍照成功
Log.info(this.TAG, 'takePhoto successfully, takePhotoResult uri: ' + result.want.uri);
return result.want.uri;
}
}).catch((error) => {
Log.info(this.TAG, `startAbility error , ${JSON.stringify(error)}`);
return error;
}).then(async (uri: string) => {
const mediaBean = await this.buildMediaBean(uri);
return mediaBean;
});
}
/**
* 封裝多媒體實體類
*
* @param uri 文件路徑
*/
private async buildMediaBean(uri: string): Promise<MediaBean> {
if (StringUtils.isNullOrEmpty(uri)) {
return null;
}
const mediaBean: MediaBean = new MediaBean();
mediaBean.localUrl = uri;
await this.appendFileInfoToMediaBean(mediaBean, uri);
return mediaBean;
}
/**
* 通過Uri查找所選文件信息,插入到MediaBean中
* @param mediaBean
* @param uri
*/
private async appendFileInfoToMediaBean(mediaBean: MediaBean, uri: string) {
if (StringUtils.isNullOrEmpty(uri)) {
return;
}
let fileList: Array<mediaLibrary.FileAsset> = [];
const parts: string[] = uri.split('/');
const id: string = parts.length > 0 ? parts[parts.length - 1] : '-1';
try {
let media = mediaLibrary.getMediaLibrary(this.mContext);
let mediaFetchOptions: mediaLibrary.MediaFetchOptions = {
selections: mediaLibrary.FileKey.ID + '= ?',
selectionArgs: [id],
uri: uri
};
let fetchFileResult = await media.getFileAssets(mediaFetchOptions);
Log.info(this.TAG, `fileList getFileAssetsFromType fetchFileResult.count = ${fetchFileResult.getCount()}`);
fileList = await fetchFileResult.getAllObject();
fetchFileResult.close();
await media.release();
} catch (e) {
Log.error(this.TAG, "query: file data exception ");
}
if (fileList && fileList.length > 0) {
let fileInfoObj = fileList[0];
Log.info(this.TAG, `file id = ${JSON.stringify(fileInfoObj.id)} , uri = ${JSON.stringify(fileInfoObj.uri)}`);
Log.info(this.TAG, `file fileList displayName = ${fileInfoObj.displayName} ,size = ${fileInfoObj.size} ,mimeType = ${fileInfoObj.mimeType}`);
mediaBean.fileName = fileInfoObj.displayName;
mediaBean.fileSize = fileInfoObj.size;
mediaBean.fileType = fileInfoObj.mimeType;
}
}
}
MediaHelper 類定義了5個方法造虎,
-
selectPicture 提供選擇圖片功能
我們通過系統(tǒng)組件 picker.PhotoViewPicker 來進行圖片選擇傅蹂,通過配置PhotoSelectOptions,指定選擇的MIMEType類型(這里PhotoViewMIMETypes.IMAGE_TYPE 圖片類型) 算凿、選擇的圖片最大數(shù)量 maxSelectNumber 份蝴,這里我們實現(xiàn)單選功能,數(shù)值設置為1即可澎媒。使用photoPicker.select 拉起系統(tǒng)組件進行選擇搞乏,然后在回調中獲取圖片的uri。
-
selectFile 提供選擇文件功能
選擇文件的功能戒努,我們通過系統(tǒng)組件 picker.DocumentViewPicker來進行文件選擇请敦,代碼基本是跟圖片選擇是一樣的,區(qū)別在于DocumentSelectOptions储玫,目前api9并沒有配置項提供侍筛,具體關注后續(xù)的api版本情況。
-
takePhoto 提供拍照功能
拍照的功能撒穷,我們也是拉起相機來進行拍照的匣椰,我們使用 startAbilityForResult 方法 + 配置拉起action (wantConstant.Action.ACTION_IMAGE_CAPTURE)的方式拉起系統(tǒng)相機,拍照結束后端礼,在then中接收返回的數(shù)據(jù)禽笑,我們通過返回碼result.resultCode 來判斷是否進行了拍照入录,如果狀態(tài)值===0,說明進行了拍照佳镜,我們再使用result.want.uri獲取拍照后的照片uri僚稿。
-
buildMediaBean 內部方法,提供MediaBean對象封裝
這個方法的作用主要是封裝一個多媒體實體類蟀伸,并觸發(fā)appendFileInfoToMediaBean 獲取Uri對應文件的一些文件信息蚀同。代碼很簡單,相信大家一目了然啊掏。
-
appendFileInfoToMediaBean 內部方法蠢络,提供追加查詢所選文件的文件信息的功能
這個方法的作用主要是通過uri查詢文件的詳細信息,包括文件名稱迟蜜、文件大小刹孔、文件類型。通過Uri獲取文件ID小泉。使用mediaLibrary.getMediaLibrary獲取media對象芦疏。配置MediaFetchOptions冕杠,主要是ID微姊,通過文件ID來查找文件對象。使用media.getFileAssets查詢文件對象結果分预,這里可以是批量操作兢交,得到一個FetchFileResult對象。遍歷FileAsset數(shù)組笼痹,得到文件信息配喳。
這里列下FileAsset的一些字段:
通過系統(tǒng)組件選擇圖片、文件或者拍照之后凳干,系統(tǒng)只是簡單的返回一個文件的Uri晴裹,如果我們需要展示文件的名稱、文件大小救赐、文件類型涧团,需要通過appendFileInfoToMediaBean 方法另外去獲取。
API標記棄用問題
上面的代碼经磅,在api9實測是可以正常使用的泌绣,但是有一些API被標記為過期,有一些在官方文檔注明即將停用预厌,但是我沒有找到可以平替的API阿迈,如果有讀者知道的,麻煩評論區(qū)告訴我一聲轧叽,謝謝苗沧。
-
ohos.app.ability.wantConstant
官方提示讓我們切換到 ohos.app.ability.wantConstant這個類下刊棕,可是我們用到wantConstant.Action,這個Action在 ohos.app.ability.wantConstant中沒有定義待逞,我在SDK中也沒有找到Action在哪一個類中定義鞠绰;
-
mediaLibrary.getMediaLibrary.getFileAssets
我們需要使用getMediaLibrary獲取多媒體對象,調用getFileAssets查詢文件的多媒體信息飒焦,官方提示讓我們使用ohos.file.picker蜈膨,奇怪的是picker中沒有getFileAssets 相關的方法,不知道官方基于什么考慮牺荠,可能后續(xù)API10會增加相應方法支持吧翁巍。那我們通過picker只能拿到一個文件的Uri,文件名稱、文件大小這些常規(guī)的文件相關的數(shù)據(jù)都拿不到休雌,那功能都無法開發(fā)灶壶,這也是我之前的一個疑問。
動態(tài)申請多媒體訪問權限
我們讀取文件的多媒體信息需要申請一個多媒體的讀取權限 ohos.permission.READ_MEDIA杈曲,這個權限需要在
module.json5中添加配置requestPermissions驰凛,在該節(jié)點下配置READ_MEDIA權限,具體如下圖:
由于這個READ_MEDIA權限需要進行動態(tài)權限申請担扑,因為還需要我們進行動態(tài)權限申請代碼邏輯開發(fā)恰响,這里由于篇幅原因,我就不過多贅述涌献,后續(xù)如果對這塊動態(tài)權限申請有不明白的地方胚宦,我再重新寫一篇文章介紹,講下動態(tài)申請權限燕垃,跳轉系統(tǒng)權限設置頁配置權限這些功能具體如何實現(xiàn)枢劝。
這次的Demo,我們直接安裝后卜壕,在系統(tǒng)設置中找到應用您旁,把對應的權限開啟即可(繞過權限動態(tài)申請)。
實現(xiàn)選擇圖片顯示功能
下面我們編寫UI頁面轴捎,使用我們上面的MediaHelper工具類選擇圖片鹤盒、拍照,并將圖片顯示出來轮蜕。
我們在Index.ets文件中放三個按鈕昨悼,以及顯示文件名稱、大小跃洛、文件類型以及文件路徑率触、顯示圖片的控件。
完整的代碼如下:
import common from '@ohos.app.ability.common';
import { MediaBean } from '../bean/MediaBean';
import { MediaHelper } from '../helper/MediaHelper';
@Entry
@Component
struct Index {
@State mediaBean: MediaBean = new MediaBean();
private mediaHelper: MediaHelper = new MediaHelper(getContext());
build() {
Row() {
Column() {
Text('選擇圖片')
.textAlign(TextAlign.Center)
.width(200)
.fontSize(16)
.padding(10)
.margin(20)
.border({ width: 0.5, color: '#ff38f84b', radius: 15 })
.onClick(() => {
this.handleClick(MediaOption.Picture)
})
Text('選擇文件')
.textAlign(TextAlign.Center)
.width(200)
.fontSize(16)
.padding(10)
.margin(20)
.border({ width: 0.5, color: '#ff38f84b', radius: 15 })
.onClick(() => {
this.handleClick(MediaOption.File)
})
Text('拍照')
.textAlign(TextAlign.Center)
.width(200)
.fontSize(16)
.padding(10)
.margin(20)
.border({ width: 0.5, color: '#ff38f84b', radius: 15 })
.onClick(() => {
this.handleClick(MediaOption.TakePhoto)
})
Divider()
.width('100%')
.height(0.5)
.color('#ff99f6a2')
.margin({ top: 20 })
.padding({ left: 20, right: 20 })
Text(`文件名稱: ${this.mediaBean.fileName ? this.mediaBean.fileName : ''}`)
.textAlign(TextAlign.Center)
.width('100%')
.fontSize(16)
.margin(10)
Text(`文件大小: ${this.mediaBean.fileSize ? this.mediaBean.fileSize : ''}`)
.textAlign(TextAlign.Center)
.width('100%')
.fontSize(16)
.margin(10)
Text(`文件類型: ${this.mediaBean.fileType ? this.mediaBean.fileType : ''}`)
.textAlign(TextAlign.Center)
.width('100%')
.fontSize(16)
.margin(10)
Text(`文件Uri: ${this.mediaBean.localUrl ? this.mediaBean.localUrl : ''}`)
.textAlign(TextAlign.Center)
.width('100%')
.fontSize(16)
.margin(10)
Image(this.mediaBean.localUrl)
.width(300)
.height(300)
.backgroundColor(Color.Grey)
}
.width('100%')
.height('100%')
}
.height('100%')
}
async handleClick(option: MediaOption) {
let mediaBean: MediaBean;
switch (option) {
case MediaOption.Picture:
mediaBean = await this.mediaHelper.selectPicture();
break;
case MediaOption.File:
mediaBean = await this.mediaHelper.selectFile();
break;
case MediaOption.TakePhoto:
mediaBean = await this.mediaHelper.takePhoto(getContext() as common.UIAbilityContext);
break;
default:
break;
}
if (mediaBean) {
this.mediaBean = mediaBean;
}
}
}
enum MediaOption {
Picture = 0,
File = 1,
TakePhoto = 2
}
打包測試
打包安裝到真機上汇竭,需要我們給項目配置簽名信息葱蝗。我們點擊File -> Project Structure ->Project ,選擇 Signing Configs面板穴张,勾選 Support HarmonyOS 跟Automatically generate signature,自動生成調試簽名两曼,生成完畢后皂甘,運行安裝到手機上。
注意:由于我們沒有實現(xiàn)多媒體讀取權限動態(tài)申請權限悼凑,因此需要在手機系統(tǒng)設置-應用中找到該應用偿枕,開啟多媒體權限,該權限默認是禁止的户辫,開啟后再打開應用操作即可渐夸。運行的具體的效果如文章開頭貼圖展示一般。