harmony 鸿蒙拉起图片编辑类应用(startAbilityByType)
拉起图片编辑类应用(startAbilityByType)
使用场景
当应用自身不具备图片编辑能力、但存在图片编辑的场景时,可以通过startAbilityByType拉起图片编辑类应用扩展面板,由对应的应用完成图片编辑操作。图片编辑类应用可以通过PhotoEditorExtensionAbility实现图片编辑页面,并将该页面注册到图片编辑面板,从而将图片编辑能力开放给其他应用。
流程示意图如下:
例如:用户在图库App中选择编辑图片时,图库App可以通过startAbilityByType拉起图片编辑类应用扩展面板。用户可以从已实现PhotoEditorExtensionAbility应用中选择一款,并进行图片编辑。
接口说明
接口详情参见PhotoEditorExtensionAbility和PhotoEditorExtensionContext。
接口名 | 描述 |
---|---|
onStartContentEditing(uri: string, want:Want, session: UIExtensionContentSession):void | 可以执行读取原始图片、加载页面等操作。 |
saveEditedContentWithImage(pixelMap: image.PixelMap, option: image.PackingOption): Promise<AbilityResult> | 传入编辑过的图片的PixelMap对象并保存。 |
图片编辑类应用实现图片编辑页面
在DevEco Studio工程中手动新建一个PhotoEditorExtensionAbility。
- 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录,如PhotoEditorExtensionAbility。
- 在PhotoEditorExtensionAbility目录中,右键选择“New > File”,新建一个.ets文件,如ExamplePhotoEditorAbility.ets。
在ExamplePhotoEditorAbility.ets中重写onCreate、onForeground、onBackground、onDestroy和onStartContentEditing的生命周期回调。
其中,需要在onStartContentEditing中加载入口页面文件pages/Index.ets,并将session、uri、实例对象等保存在LocalStorage中传递给页面。
import { PhotoEditorExtensionAbility,UIExtensionContentSession,Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; const TAG = '[ExamplePhotoEditorAbility]'; export default class ExamplePhotoEditorAbility extends PhotoEditorExtensionAbility { onCreate() { hilog.info(0x0000, TAG, 'onCreate'); } // 获取图片,加载页面并将需要的参数传递给页面 onStartContentEditing(uri: string, want: Want, session: UIExtensionContentSession): void { hilog.info(0x0000, TAG, `onStartContentEditing want: ${JSON.stringify(want)}, uri: ${uri}`); const storage: LocalStorage = new LocalStorage({ "session": session, "uri": uri } as Record<string, Object>); session.loadContent('pages/Index', storage); } onForeground() { hilog.info(0x0000, TAG, 'onForeground'); } onBackground() { hilog.info(0x0000, TAG, 'onBackground'); } onDestroy() { hilog.info(0x0000, TAG, 'onDestroy'); } }
在page中实现图片编辑功能。
图片编辑完成后调用saveEditedContentWithImage保存图片,并将回调结果通过terminateSelfWithResult返回给调用方。
import { common } from '@kit.AbilityKit'; import { UIExtensionContentSession, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { fileIo } from '@kit.CoreFileKit'; import { image } from '@kit.ImageKit'; const storage = LocalStorage.getShared() const TAG = '[ExamplePhotoEditorAbility]'; @Entry @Component struct Index { @State message: string = 'editImg'; @State originalImage: PixelMap|null = null; @State editedImage: PixelMap|null = null; private newWant ?: Want; aboutToAppear(): void { let originalImageUri = storage?.get<string>("uri") ?? ""; hilog.info(0x0000, TAG, `OriginalImageUri: ${originalImageUri}.`); this.readImageByUri(originalImageUri).then(imagePixMap => { this.originalImage = imagePixMap; }) } // 根据uri读取图片内容 async readImageByUri(uri: string): Promise < PixelMap|null > { hilog.info(0x0000, TAG, "uri: " + uri); let file: fileIo.File|undefined; try { file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY); hilog.info(0x0000, TAG, "Original image file id: " + file.fd); let imageSourceApi: image.ImageSource = image.createImageSource(file.fd); if(!imageSourceApi) { hilog.info(0x0000, TAG, "ImageSourceApi failed"); return null; } let pixmap: image.PixelMap = await imageSourceApi.createPixelMap(); if(!pixmap) { hilog.info(0x0000, TAG, "createPixelMap failed"); return null; } this.originalImage = pixmap; return pixmap; } catch(e) { hilog.info(0x0000, TAG, `ReadImage failed:${e}`); } finally { fileIo.close(file); } return null; } build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) Button("RotateAndSaveImg").onClick(event => { hilog.info(0x0000, TAG, `Start to edit image and save.`); // 编辑图片功能实现 this.originalImage?.rotate(90).then(() => { let packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 }; try { // 调用saveEditedContentWithImage保存图片 (getContext(this) as common.PhotoEditorExtensionContext).saveEditedContentWithImage(this.originalImage as image.PixelMap, packOpts).then(data => { if (data.resultCode == 0) { hilog.info(0x0000, TAG, `Save succeed.`); } hilog.info(0x0000, TAG, `saveContentEditingWithImage result: ${JSON.stringify(data)}`); this.newWant = data.want; // data.want.uri存有编辑过图片的uri this.readImageByUri(this.newWant?.uri ?? "").then(imagePixMap => { this.editedImage = imagePixMap; }) }) } catch (e) { hilog.error(0x0000, TAG, `saveContentEditingWithImage failed:${e}`); return; } }) }).margin({ top: 10 }) Button("terminateSelfWithResult").onClick((event => { hilog.info(0x0000, TAG, `Finish the current editing.`); let session = storage.get('session') as UIExtensionContentSession; // 关闭并回传修改结果给调用方 session.terminateSelfWithResult({ resultCode: 0, want: this.newWant }); })).margin({ top: 10 }) Image(this.originalImage).width("100%").height(200).margin({ top: 10 }).objectFit(ImageFit.Contain) Image(this.editedImage).width("100%").height(200).margin({ top: 10 }).objectFit(ImageFit.Contain) } .width('100%') } .height('100%') .backgroundColor(Color.Pink) .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) } }
在工程Module对应的module.json5配置文件中注册PhotoEditorExtensionAbility。
type标签需要配置为”photoEditor”,srcEntry需要配置为PhotoEditorExtensionAbility组件所对应的代码路径。
{ "module": { "extensionAbilities": [ { "name": "ExamplePhotoEditorAbility", "icon": "$media:icon", "description": "ExamplePhotoEditorAbility", "type": "photoEditor", "exported": true, "srcEntry": "./ets/PhotoEditorExtensionAbility/ExamplePhotoEditorAbility.ets", "label": "$string:EntryAbility_label", "extensionProcessMode": "bundle" }, ] } }
调用方拉起图片编辑类应用编辑图片
开发者可以在UIAbility或者UIExtensionAbility的页面中通过接口startAbilityByType拉起图片编辑类应用扩展面板,系统将自动查找并在面板上展示基于PhotoEditorExtensionAbility实现的图片编辑应用,由用户选择某个应用来完成图片编辑的功能,最终将编辑的结果返回给到调用方,具体步骤如下:
导入模块。
import { common, wantConstant } from '@kit.AbilityKit'; import { fileUri, picker } from '@kit.CoreFileKit';
(可选)实现从图库中选取图片。
async photoPickerGetUri(): Promise < string > { try { let PhotoSelectOptions = new picker.PhotoSelectOptions(); PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; PhotoSelectOptions.maxSelectNumber = 1; let photoPicker = new picker.PhotoViewPicker(); let photoSelectResult: picker.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions); return photoSelectResult.photoUris[0]; } catch(error) { let err: BusinessError = error as BusinessError; hilog.info(0x0000, TAG, 'PhotoViewPicker failed with err: ' + JSON.stringify(err)); } return ""; }
将图片拷贝到本地沙箱路径。
let context = getContext(this) as common.UIAbilityContext; let file: fileIo.File|undefined; try { file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY); hilog.info(0x0000, TAG, "file: " + file.fd); let timeStamp = Date.now(); // 将用户图片拷贝到应用沙箱路径 fileIo.copyFileSync(file.fd, context.filesDir + `/original-${timeStamp}.jpg`); this.filePath = context.filesDir + `/original-${timeStamp}.jpg`; this.originalImage = fileUri.getUriFromPath(this.filePath); } catch (e) { hilog.info(0x0000, TAG, `readImage failed:${e}`); } finally { fileIo.close(file); }
在startAbilityByType回调函数中,通过want.uri获取编辑后的图片uri,并做对应的处理。
let context = getContext(this) as common.UIAbilityContext; let abilityStartCallback: common.AbilityStartCallback = { onError: (code, name, message) => { const tip: string = `code:` + code + ` name:` + name + ` message:` + message; hilog.error(0x0000, TAG, "startAbilityByType:", tip); }, onResult: (result) => { // 获取到回调结果中编辑后的图片uri并做对应的处理 let uri = result.want?.uri ?? ""; hilog.info(0x0000, TAG, "PhotoEditorCaller result: " + JSON.stringify(result)); this.readImage(uri).then(imagePixMap => { this.editedImage = imagePixMap; }); } }
将图片转换为图片uri,并调用startAbilityByType拉起图片编辑应用面板。
let uri = fileUri.getUriFromPath(this.filePath); context.startAbilityByType("photoEditor", { "ability.params.stream": [uri], // 原始图片的uri,只支持传入一个uri "ability.want.params.uriPermissionFlag": wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION // 至少需要分享读权限给到图片编辑面板 } as Record<string, Object>, abilityStartCallback, (err) => { let tip: string; if (err) { tip = `Start error: ${JSON.stringify(err)}`; hilog.error(0x0000, TAG, `startAbilityByType: fail, err: ${JSON.stringify(err)}`); } else { tip = `Start success`; hilog.info(0x0000, TAG, "startAbilityByType: ", `success`); } });
示例:
import { common, wantConstant } from '@kit.AbilityKit';
import { fileUri, picker } from '@kit.CoreFileKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { fileIo } from '@kit.CoreFileKit';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
const TAG = 'PhotoEditorCaller';
@Entry
@Component
struct Index {
@State message: string = 'selectImg';
@State originalImage: ResourceStr = "";
@State editedImage: PixelMap|null = null;
private filePath: string = "";
// 根据uri读取图片内容
async readImage(uri: string): Promise < PixelMap|null > {
hilog.info(0x0000, TAG, "image uri: " + uri);
let file: fileIo.File|undefined;
try {
file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
hilog.info(0x0000, TAG, "file: " + file.fd);
let imageSourceApi: image.ImageSource = image.createImageSource(file.fd);
if(!imageSourceApi) {
hilog.info(0x0000, TAG, "imageSourceApi failed");
return null;
}
let pixmap: image.PixelMap = await imageSourceApi.createPixelMap();
if(!pixmap) {
hilog.info(0x0000, TAG, "createPixelMap failed");
return null;
}
this.editedImage = pixmap;
return pixmap;
} catch(e) {
hilog.info(0x0000, TAG, `readImage failed:${e}`);
} finally {
fileIo.close(file);
}
return null;
}
// 图库中选取图片
async photoPickerGetUri(): Promise < string > {
try {
let PhotoSelectOptions = new picker.PhotoSelectOptions();
PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
PhotoSelectOptions.maxSelectNumber = 1;
let photoPicker = new picker.PhotoViewPicker();
let photoSelectResult: picker.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
hilog.info(0x0000, TAG,
'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));
return photoSelectResult.photoUris[0];
} catch(error) {
let err: BusinessError = error as BusinessError;
hilog.info(0x0000, TAG, 'PhotoViewPicker failed with err: ' + JSON.stringify(err));
}
return "";
}
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button("selectImg").onClick(event => {
// 图库中选取图片
this.photoPickerGetUri().then(uri => {
hilog.info(0x0000, TAG, "uri: " + uri);
let context = getContext(this) as common.UIAbilityContext;
let file: fileIo.File|undefined;
try {
file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
hilog.info(0x0000, TAG, "file: " + file.fd);
let timeStamp = Date.now();
// 将用户图片拷贝到应用沙箱路径
fileIo.copyFileSync(file.fd, context.filesDir + `/original-${timeStamp}.jpg`);
this.filePath = context.filesDir + `/original-${timeStamp}.jpg`;
this.originalImage = fileUri.getUriFromPath(this.filePath);
} catch (e) {
hilog.info(0x0000, TAG, `readImage failed:${e}`);
} finally {
fileIo.close(file);
}
})
}).width('200').margin({ top: 20 })
Button("editImg").onClick(event => {
let context = getContext(this) as common.UIAbilityContext;
let abilityStartCallback: common.AbilityStartCallback = {
onError: (code, name, message) => {
const tip: string = `code:` + code + ` name:` + name + ` message:` + message;
hilog.error(0x0000, TAG, "startAbilityByType:", tip);
},
onResult: (result) => {
// 获取到回调结果中编辑后的图片uri并做对应的处理
let uri = result.want?.uri ?? "";
hilog.info(0x0000, TAG, "PhotoEditorCaller result: " + JSON.stringify(result));
this.readImage(uri).then(imagePixMap => {
this.editedImage = imagePixMap;
});
}
}
// 将图片转换为图片uri,并调用startAbilityByType拉起图片编辑应用面板
let uri = fileUri.getUriFromPath(this.filePath);
context.startAbilityByType("photoEditor", {
"ability.params.stream": [uri], // 原始图片的uri,只支持传入一个uri
"ability.want.params.uriPermissionFlag": wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION // 至少需要分享读权限给到图片编辑面板
} as Record<string, Object>, abilityStartCallback, (err) => {
let tip: string;
if (err) {
tip = `Start error: ${JSON.stringify(err)}`;
hilog.error(0x0000, TAG, `startAbilityByType: fail, err: ${JSON.stringify(err)}`);
} else {
tip = `Start success`;
hilog.info(0x0000, TAG, "startAbilityByType: ", `success`);
}
});
}).width('200').margin({ top: 20 })
Image(this.originalImage).width("100%").height(200).margin({ top: 20 }).objectFit(ImageFit.Contain)
Image(this.editedImage).width("100%").height(200).margin({ top: 20 }).objectFit(ImageFit.Contain)
}
.width('100%')
}
.height('100%')
.backgroundColor(Color.Orange)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
}
你可能感兴趣的鸿蒙文章
harmony 鸿蒙FA模型访问Stage模型DataShareExtensionAbility
- 所属分类: 后端技术
- 本文标签:
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦