1. 项目介绍

在日常生活中有很多APP都有图片编辑的功能,但由于APP间存在一定的功能差异,开发者往往要为每个APP单独实现一套图片编辑逻辑,使APP的开发成本大大增加。本篇Codelab将为开发者介绍DevEco中集成的图片编辑模板,旨在为开发者提供一个图片编辑模块开发的"地基",开发者只需要在此基础上添加业务所需的自定义图片编辑算法,从而减少代码量。
本模板主要分为两个部分:界面UI和图片编辑器。
模板界面UI部分主要为开发者提供了:图片编辑界面的设计参考,以及HarmonyOS界面UI开发样例工程。图片编辑器部分提供了实现图片编辑功能的框架以及两个样例图片编辑功能。
效果预览:

  1. 顶部App Bar部分:后退(Back)、撤销(Undo)、重做(Redo)以及保存(Save)按钮。、
  2. 中间区域BackGroundLayer以及CropPicker用于预览图显示以及裁剪框绘制。
  3. 底部区域由下往上分为三级菜单,一级菜单用于选择图片编辑功能,二级菜单用于选择次级功能,三级菜单一般用于调节次级功能的编辑参数。

编辑功能展示:

拖动裁剪框,裁剪区域外的图片绘制阴影效果。

预览效果满意后,切换其他功能来确认更改。点击撤销(Undo)按钮,返回上一步预览状态。

  1. 代码结构解读
    animation: 动效目录,存放图片编辑过程中的各类动效实现类。
    exceptions:异常目录,存放各类编辑异常。
    handler:图片编辑器核心逻辑目录,包含图片编辑器、图片编辑代理、编辑操作消息队列游戏实现。
    strategy:图片编辑功能实现目录。action中存放编辑器可识别的编辑IEditAction;bean中存放编辑操作所需参数;imp中存放实现action所对应的图片编辑strategy。
    utils:工具类存放目录。
    view:自定义控件存放目录。
  2. 相关权限

使用本模板时如使用用户外部存储中的媒体文件信息,需要申请以下权限,应用权限的申请可以参考权限章节。
ohos.permission.READ_MEDIA:用于允许应用读取用户外部存储中的媒体文件信息。

    {
      "name": "ohos.permission.READ_MEDIA"
      "reason": "$string:read_media"
    }
]
  1. 图片编辑器详解
    EditActionFactory:Action工厂类,提供createAction方法用于创建图片编辑Action。
    EditActionPipeline:图片编辑Action队列,用于维持图片编辑操作的时序。
    EditStrategyManager: 图片编辑Strategy管理器。
    ImageEditorImp:图片编辑器实现类,从Pipeline接收processEvent操作指令,调用Action对应Strategy对图片进行编辑。
    ImageEdtiorProxy:图片编辑引擎,本模板中挂载于ImageEditView上,接收用户编辑操作,转化为Action放入Action队列中。
  1. 添加自定义图片编辑功能

本模板中预置了两种图片编辑功能:裁剪与调节,开发者也可以自行添加业务所需的其他图片编辑功能,比如滤镜、涂鸦等。添加的方式如下:

实现IEditStrategy。

    /**
     * Image processing method
     *
     * @param <T> Specifies the parameter type, which is defined by the image editing algorithm.
     * @param origin Source PixelMap
     * @param params Image editing algorithm parameters.
     * @return PixelMap after edit
     * @throws HandleStrategyException Handle exception
     */
     <T> PixelMap handle(final PixelMap origin, EditParams<T> params) throws HandleStrategyException;

    /**
     * Obtains the strategy name.
     *
     * @return Strategy name
     */
     String getName();

    /**
     * Create an corresponding action instance
     *
     * @return An empty action instance
     */
     IEditAction createAction();
    }

实现IEditAction。

    enum ActionType {
        ACT_EDIT(0x01), ACT_UNDO(0x11), ACT_REDO(0x12), ACT_DROP(0x14), ACT_EXPORT(0x15);
        private int value = 0;
        ActionType(int value) {
            this.value = value;
        }
        public int getValue() {
            return this.value;
        }
    }
    /**
     * Execute an edit-action against the PixelMap of which the content will be modified
     *
     * @param bmp the PixelMap to be modified
     * @return the output PixelMap after process
     * @throws EditActionException execution failed, the PixelMap should not be dropped because of dirty data
     */
    PixelMap execute(PixelMap bmp) throws EditActionException;

    ActionType getActionType();

    <T> void setParams(T params);
}

在ImageEditProxy中挂载相应的Strategy

private ImageEditorProxy createImageEditProxy(String imgFilePath, ImageEditView editorView)
    throws EditActionException {
    imageEditorImp = new ImageEditorImp(imgFilePath);
    // Add ImageEdit strategy here
    imageEditorImp.addEditStrategy(new CropEditStrategy());
    imageEditorImp.addEditStrategy(new BrightnessStrategy());
    imageEditorImp.addEditStrategy(new SaturationStrategy());
    imageEditorImp.addEditStrategy(new ContrastStrategy());
    // Bind image editor to proxy
    final ImageEditorProxy editorProxy = new ImageEditorProxy(imageEditorImp);
    // Observe proxy
    editorProxy.addObserver(this);
    return editorProxy;
    }
  1. 内置图片编辑功能实现介绍

此模板中内置的两种图片编辑功能也实现了上述的两个接口,为开发者提供两种不同类型的编辑功能实现样例。
裁剪功能实现:

@Override
public <T> PixelMap handle(final PixelMap origin, EditParams<T> params) throws HandleStrategyException {
    Rect operatingRect = handleParameters(origin, params);
    PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
    options.pixelFormat = PixelFormat.ARGB_8888;
    options.size = new Size(operatingRect.width, operatingRect.height);
    return PixelMap.create(origin, operatingRect, options);
}
private <T> Rect handleParameters(final PixelMap origin, EditParams<T> options) throws HandleStrategyException {
    validateParameter(origin, options);
    Rect cropRect = options.getProperty(Rect.class, "cropRect");
    cropRect.width = cropRect.width - cropRect.minX;
    cropRect.height = cropRect.height - cropRect.minY;
    return getFinalCropRect(cropRect, origin);
}
03-05 21:07