HarmonyOS - 本地相冊的糾葛
前言
二月再見,三月你好,陽春三月萬物復蘇,愿一切美好都如約而至。攜手共創,鴻蒙社區。前幾天有個同事問我如何把圖片存在系統相冊的圖片,當時我就懵逼了,鴻蒙的好像真的不怎么懂?而且這個操作在我們平時開發時也經常用到,所以搞起。
效果展示
踩坑之路
應該官網有介紹吧,去官網看看,發現是有一丟丟介紹
附上鏈接:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-data-mgmt-storage-0000001050994909
都說安卓和鴻蒙差不多,應該思路是差不多的吧,于是找到一片文章(https://harmonyos.51cto.com/posts/10568)里面有MediaStore類,用于操作系統媒體數據庫的類,鴻蒙確實也有個類似的類AVStorage,但是現在開放的功能不如MediaStore強大。
后面發現是鴻蒙的設計思路有點像ios的,每個應用的都有獨自沙河目錄,每個app的數據都存儲在當前的應用當中,這樣大大的確保數據的隱蔽性和安全性,這樣比安卓安全性好很多。
保存圖片到系統相冊
demo布局:
//展示圖片
<Image
ohos:id="$+id:show_photo"
ohos:height="200fp"
ohos:width="200fp"
ohos:image_src="$media:empty"
ohos:scale_mode="zoom_center"
ohos:top_margin="30fp"
/>
//選擇圖片
<Text
ohos:id="$+id:select_photo"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="選擇圖片"
ohos:text_size="20vp"
ohos:top_margin="10fp"
/>
//保存圖片
<Text
ohos:top_margin="10fp"
ohos:id="$+id:save_photo"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="保存圖片"
ohos:text_size="20vp"
/>
效果如圖:
涉及權限
config.json權限配置如下:
"reqPermissions": [
{"name": "ohos.permission.READ_USER_STORAGE"},
{"name": "ohos.permission.WRITE_USER_STORAGE"}
]
動態申請權限
需要動態申請這兩個權限,申請時會有權限彈窗,不寫的話,不會有權限彈窗,但是也是可以使用的。
String[] permissions = {"ohos.permission.READ_USER_STORAGE", "ohos.permission.WRITE_USER_STORAGE"};
requestPermissionsFromUser(permissions, 0);
保存圖片
獲取到權限之后,就可以保存圖片到系統相冊了,我們媒體的增刪改查都需要用到DataAbilityHelper和AVStorage。
//保存圖片到相冊 fileName文件名 PixelMap 圖片數據
private void saveImageToLibrary(String fileName, PixelMap pixelMap) {
try {
ValuesBucket valuesBucket = new ValuesBucket();
//文件名
valuesBucket.putString(AVStorage.Images.Media.DISPLAY_NAME, fileName);
//相對路徑
valuesBucket.putString("relative_path", "DCIM/");
//文件格式,類型要一定要注意要是JPEG,PNG類型不支持
valuesBucket.putString(AVStorage.Images.Media.MIME_TYPE, "image/JPEG");
//應用獨占:is_pending設置為1時表示只有該應用能訪問此圖片,其他應用無法發現該圖片,當圖片處理操作完成后再吧is_pending設置為0,解除獨占,讓其他應用可見
valuesBucket.putInteger("is_pending", 1);
//鴻蒙的helper.insert方法和安卓的contentResolver.insert方法有所不同,安卓方法直接返回一個uri,我們就可以拿來直接操作,而鴻蒙方法返回官方描述是Returns the index of the inserted data record(返回插入的數據記錄的索引),這個index我的理解就是id,因此,我們需要自己在后面拼出文件的uri再進行操作
DataAbilityHelper helper = DataAbilityHelper.creator(this);
int index = helper.insert(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, valuesBucket);
Uri uri = Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(index));
//獲取到uri后,安卓通過contentResolver.openOutputStream(uri)就能獲取到輸出流來寫文件,而鴻蒙沒有提供這樣的方法,我們就只能通過uri獲取FileDescriptor,再通過FileDescriptor生成輸出流打包編碼成新的圖片文件,這里helper.openFile方法一定要有“w”寫模式,不然會報FileNotFound的錯誤。
FileDescriptor fd = helper.openFile(uri, "w");
ImagePacker imagePacker = ImagePacker.create();
ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
OutputStream outputStream = new FileOutputStream(fd);
packingOptions.format = "image/jpeg";
packingOptions.quality = 90;
boolean result = imagePacker.initializePacking(outputStream, packingOptions);
if (result) {
result = imagePacker.addImage(pixelMap);
if (result) {
long dataSize = imagePacker.finalizePacking();
}
}
outputStream.flush();
outputStream.close();
valuesBucket.clear();
//解除獨占
valuesBucket.putInteger("is_pending", 0);
helper.update(uri, valuesBucket, null);
} catch (Exception e) {
e.printStackTrace();
}
}
效果如下
讀取本地相冊圖片
在config.json中配置讀取文件權限(ohos.permission.READ_USER_STORAGE)
"reqPermissions": [{"name": "ohos.permission.READ_USER_STORAGE"}]
在ability中手動申請權限
String[] permissions = {"ohos.permission.READ_USER_STORAGE"};
requestPermissionsFromUser(permissions, 0);
彈出數據來源選擇框,獲取數據來源的方式。
//選擇圖片
private void selectPhoto() {
//調起系統的選擇來源數據視圖
Intent intent = new Intent();
Operation opt=new Intent.OperationBuilder().withAction("android.intent.action.GET_CONTENT").build();
intent.setOperation(opt);
intent.addFlags(Intent.FLAG_NOT_OHOS_COMPONENT);
intent.setType("image/*");
startAbilityForResult(intent, imgRequestCode);
}
效果如圖:
下面是選擇圖片的回調,imgRequestCode字段的是自定義的,必須是int的類型,這個字段是和上面的selectPhoto()方法里面的imgRequestCode是一致的,根據這個imgRequestCode來判斷是否從選擇圖片的回調回來的,
/*選擇圖片回調*/
@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
if(requestCode==imgRequestCode && resultData!=null)
{
//選擇的Img對應的Uri
String chooseImgUrl=resultData.getUriString();
//定義數據能力幫助對象
DataAbilityHelper helper=DataAbilityHelper.creator(getContext());
//定義圖片來源對象
ImageSource imageSource = null;
//獲取選擇的Img對應的Id
String chooseImgId=null;
//如果是選擇文件則getUriString結果為dataability:///com.android.providers.media.documents/document/image%3A437,其中%3A437是":"的URL編碼結果,后面的數字就是image對應的Id
//如果選擇的是圖庫則getUriString結果為dataability:///media/external/images/media/262,最后就是image對應的Id
//這里需要判斷是選擇了文件還是圖庫
if(chooseImgUri.lastIndexOf("%3A")!=-1){
chooseImgId = chooseImgUri.substring(chooseImgUri.lastIndexOf("%3A")+3);
}
else {
chooseImgId = chooseImgUri.substring(chooseImgUri.lastIndexOf('/')+1);
}
//獲取圖片對應的uri,由于獲取到的前綴是content,我們替換成對應的dataability前綴
Uri uri=Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,chooseImgId);
try {
//讀取圖片
FileDescriptor fd = helper.openFile(uri, "r");
imageSource = ImageSource.create(fd, null);
//創建位圖
PixelMap pixelMap = imageSource.createPixelmap(null);
//設置圖片控件對應的位圖
photo.setPixelMap(pixelMap);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (imageSource != null) {
imageSource.release();
}
}
}
}
總結
官網現有文檔不多,開發鴻蒙的時候遇到很多問題,有安卓基礎的小伙伴可以參考安卓的思路去解決,應該可以事半功倍,希望本次分享對大家有所幫助。