0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于安卓平台的图片裁切组件已实现鸿蒙化迁移和重构

OpenHarmony技术社区 来源:鸿蒙技术社区 作者:朱伟ISRC 2021-11-10 09:22 次阅读

基于安卓平台的图片裁切组件 crop_image_layout,实现了鸿蒙化迁移和重构,代码已经开源,目前已经获得了很多人的 Star 和 Fork ,欢迎各位下载使用并提出宝贵意见!

开源地址:
https://gitee.com/isrc_ohos/crop_image_layout_ohos

crop_image_layout_ohos 组件能对图片进行旋转和自定义裁切的操作,并且无论待裁切图片原尺寸有多大或多小,最终都将在以最佳尺寸在组件内显示。

同时,该组件操作界面简洁且使用方法简单,易被开发者使用或优化,能够提升应用的丰富性和可操作性。

组件效果展示

组件中可以通过操作图片、裁切框、按钮,最终实现在图片中裁切部分区域并进行显示的效果,组件的运行效果如图 1 所示。7da0ed0e-417f-11ec-b939-dac502259ad0.gif

图 1:crop_image_layout_ohos 组件的运行效果图

对应运行效果图,详细解释其主要提供的功能:

  • 点击“rotate”按钮可以对图片进行旋转操作。

  • 手指按住裁切框内任意处并拖动,可实现裁切框移动,裁切框停止移动时,框内的图片即为想要裁切的图片。

  • 被选中区域的左上角和右下角坐标会在图片下方的文本框中进行显示;

  • 点击“crop”按钮可对选中的图片区域进行裁切,之后会跳转到第二个界面显示裁切后的图片。

Sample 解析

①组件的整体使用流程

如下图:

7e4e15ec-417f-11ec-b939-dac502259ad0.gif

图 2:组件使用流程示意图

在介绍组件的使用前,先来介绍下构成 crop_image_layout_ohos 组件功能的3 个重要部分:
  • 裁切框:负责划定图片的裁切区域。

  • 裁切图片:是指被导入组件中,即将被裁切的图片。

  • 组件区域:是指组件所在的位置。

组件的使用过程可以概括为首先设置裁切框的位置(坐标),然后将裁切框坐标数据添加到裁切图片中,最后将裁切框和裁切图片添加到组件区域中。

在这过程中,组件还实现了对裁切图片和裁切框尺寸的适配显示效果,这部分的具体原理会在 Library 解析部分进行讲解。 ②组件的具体使用步骤

下面介绍 crop_image_layout_ohos 组件的具体使用方法,共分为 6 个步骤:
  • 步骤 1:在 xml 文件中添加 EditPhotoView 控件。

  • 步骤 2:导入所需类并实例化类对象。

  • 步骤 3:将裁切框坐标数据设置到裁切图片。

  • 步骤 4:将裁切图片和裁切框添加到布局中

  • 步骤 5:显示裁切框左上角和右下角坐标值。

  • 步骤 6:设置监听事件。

(1)在 xml 文件中添加 EditPhotoView 控件

在 xml 文件中以 com.huawei.croplayout.EditPhotoView 为控件名添加 EditPhotoView 控件,用来显示 crop_image_layout_ohos 的组件区域。

并分别设置裁切框拐角和边的颜色以及裁切图片未被选中部分的阴影颜色,如图所示。

7eb35092-417f-11ec-b939-dac502259ad0.png

图 3:属性设置示意图

//添加组件区域
ohos:id="$+id:editable_image"
...
crop:crop_corner_color="#45B4CA"//裁切框拐角颜色
crop:crop_line_color="#d7af55"//裁切框边颜色
crop:crop_shadow_color="#77ffffff"/>//裁切图片未被选中部分的阴影颜色

(2)导入所需类并实例化类对象

在 MainAbilitySlice 类的 onStart() 方法中,分别导入类:
  • onBoxChangedListener 类用于监听裁切框变化

  • EditPhotoView 类用于设置组件区域

  • EdittableImage 类用于设置裁切图片

  • ScalableBox 类用于设置裁切框

importcom.example.croplayout.handler.OnBoxChangedListener;//裁切框变化监听importcom.example.croplayout.EditPhotoView;//组件区域importcom.example.croplayout.EditableImage;//裁切图片importcom.example.croplayout.model.ScalableBox;//裁切框

创建 EditPhotoView 类和 Text 类对象分别用于绑定 crop_image_layout_ohos 的组件区域和用于显示裁切框坐标的文本控件。

实例化 EdittableImage 类对象 image,使其包含图 1 所示的裁切图片,实现组件裁切图片的导入。

创建一个元素类型为 ScalableBox 的 List,并将其命名为 boxes,用于盛纳裁切框的坐标。

新实例化一个左上角坐标为(25,180)、右下角坐标为(640,880)的裁切框对象,并调用 add() 方法将其添加到上述 boxes 中。

//用于绑定组件的裁切图片视图区域finalEditPhotoViewimageView=(EditPhotoView)findComponentById(ResourceTable.Id_editable_image);
finalTextboxText=(Text)findComponentById(ResourceTable.Id_box_text);
finalEditableImageimage=newEditableImage(this,ResourceTable.Media_photo2);
Listboxes=newArrayList<>();//用于设置裁切框的坐标
boxes.add(newScalableBox(25,180,640,880));//裁切框的坐标

(3)将裁切框坐标数据设置到裁切图片

通过 setBoxes() 方法将 boxes 中裁切框对象的坐标数据设置到裁切图片 image 中,实现裁切框相对裁切图片的位置设定。

image.setBoxes(boxes);

(4)将裁切图片和裁切框添加到布局中

调用 intView() 方法,创建裁切图片和裁切框的视图,并将其添加到组件布局中进行显示。

imageView.initView(this,image);

(5)显示裁切框左上角和右下角坐标值

重新声明一个 ScalableBox 类型的对象 activeBox,用于动态取裁切框的坐标,并将其通过 Text 在界面上显示出来。

ScalableBoxactiveBox=image.getActiveBox();//动态获取图片中裁切框选取区域的坐标
boxText.setText("box:["+activeBox.getX1()+","+activeBox.getY1()+
"],["+activeBox.getX2()+","+activeBox.getY2()+"]");

(6)设置监听事件

组件区域监听事件:为组件区域对象 imageView 设置监听事件,当裁切框位置发生变化时,将其坐标设置到 Text 对象 boxText 中进行显示。

imageView.setOnBoxChangedListener(newOnBoxChangedListener(){
@Override//设置裁切框区域监听事件
publicvoidonChanged(intx1,inty1,intx2,inty2){
boxText.setText("box:["+x1+","+y1+"],["+x2+","+y2+"]");
}
});

旋转按钮“rotate”监听事件:声明一个 Button 类对象 rotateButton 将其与“rotate_button”控件绑定。

为其设置点击监听事件,按钮被点击时,通过组件区域对象 imageView 调用 rotateImageView() 方法实现裁切图片向右旋转 90° 的效果。

ButtonrotateButton=(Button)findComponentById(ResourceTable.Id_rotate_button);//与”rorate_button“控件绑定
rotateButton.setClickedListener(newComponent.ClickedListener(){
@Override//设置点击监听事件
publicvoidonClick(Componentcomponent){
imageView.rotateImageView();//实现裁切图片向右旋转90°
}
});

裁切按钮“crop”监听事件:声明一个 Button 类对象 cropButton 将其与“crop_button”控件绑定,并设置点击监听事件。

按钮被点击时,通过裁切图片 image 调用 cropOriginalImage() 方法得到裁切后的图片并将其存放于 PixelMap 类对象中。

通过 Intent 跳转到第二个界面,并将裁切后的图片作为参数传入,显示在第二个界面中。

ButtoncropButton=(Button)findComponentById(ResourceTable.Id_crop_button);
cropButton.setClickedListener(newComponent.ClickedListener(){
@Override//设置点击监听事件
publicvoidonClick(Componentcomponent){
PixelMapcroppedImage=image.cropOriginalImage();
IntentnewIntent=newIntent();
newIntent.setParam("image",croppedImage);
present(newSecondAbilitySlice(),newIntent);
}
});

Library解析

Library 部分将围绕图 2,对 crop_image_layout_ohos 组件的原理和执行逻辑进行梳理。

其中会涉及到 ScalableBox 类、EdittableImage 类、EditPhotoView 类、SelectionView 类和 ImageHelper 类。

ScalableBox 类、EdittableImage 类、EditPhotoView 类在上一节中简单介绍过是用来设置组件区域、裁切图片和裁切框的;SelectionView 类用于用于设置裁剪框所在的视图。

ImageHelper 类相当于一个图片操作辅助工具,用来完成从原裁剪图片中获取 PixelMap 和旋转图片等图片处理操作。 ①设置裁切框(实例化其尺寸)

如下图:

7f2aa976-417f-11ec-b939-dac502259ad0.png

图 4:裁切框坐标示意图

在 Sample 解析中我们讲过,需要调用 add() 方法将新实例化的左上角坐标为(25,180)、右下角坐标为(640,880)的裁切框加入到 boxes 对象中。

其中,左上角坐标对应图 4 中的(X1,Y1),右上角对应图 4 中的(X2,Y2),通过设置裁切框对角线上两个点,就可以唯一确定其大小和位置了。

boxes.add(newScalableBox(25,180,640,880));

实例化过程需要通过 ScalableBox 类的构造函数,设置裁切框左上角和右下角的坐标。

publicScalableBox(intx1,inty1,intx2,inty2){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
}

②将裁切框坐标数据设置到裁切图片

获取裁切框列表 boxes 的第一个对象,即我们之前设置好对角线坐标的裁切框数据,并将其添加到裁切图片中。这是通过 EditableImage 类的 setBoxes() 方法实现的。

在该方法中,若裁切框对象列表 boxes 不为空且尺寸大于 0,则将 boxes 赋给 EditableImage 类的成员变量 originalBoxes,用于存储所有的裁剪框对象的数据。

将另一个此类的成员变量 copyofActiveBox 实例化,用于存储被选中的裁剪框两个角的坐标值。

其中,activeBoxIdx 是指在 boxes 中盛纳裁切框坐标的下标,List 可以为用户预留多个裁切框坐标,本组件中只是用下表为 0 的裁切框坐标。

publicvoidsetBoxes(Listboxes,intactiveBoxIdx){
if(boxes!=null&&boxes.size()>0){//如果boxes对象不为空且尺寸大于0
this.originalBoxes=boxes;
copyOfActiveBox=newScalableBox();
copyOfActiveBox.setX1(originalBoxes.get(activeBoxIdx).getX1());
copyOfActiveBox.setX2(originalBoxes.get(activeBoxIdx).getX2());
copyOfActiveBox.setY1(originalBoxes.get(activeBoxIdx).getY1());
copyOfActiveBox.setY2(originalBoxes.get(activeBoxIdx).getY2());
}
}

③将裁切图片和裁剪框添加到布局中

该功能是通过 EditPhotoView 类的 initView() 方法实现的。先根据创建 EditPhotoView 对象时传入的裁切框尺寸、边角尺寸和颜色等属性,将 SelectionView 类实例化,用于展示裁剪框的视图。

然后根据传入的裁切图片实例化得到 Image 图片类对象,用于展示裁切图片的视图;再将 SelectionView 类对象和 Image 对象的布局设置为跟随父组件,并将两者添加到裁切组件区域布局中,实现裁切框和裁切图片的显示。

分别通过 setViewSize() 方法设置裁切图片视图区域尺寸、setPixelMap() 为其设置裁切图片位图格式、setScaleMode() 方法为其设置图片缩放模式为中心缩放、setBoxSize() 方法设置裁切图片和裁切框适配后的尺寸。

publicvoidinitView(Contextcontext,EditableImageeditableImage){
this.editableImage=editableImage;
selectionView=newSelectionView(context,
lineWidth,cornerWidth,cornerLength,
lineColor,cornerColor,dotColor,shadowColor,editableImage);
imageView=newImage(context);//设置选择区域尺寸、边角尺寸以及颜色
imageView.setLayoutConfig(newLayoutConfig(LayoutConfig.MATCH_PARENT,LayoutConfig.MATCH_PARENT));//跟随父组件
selectionView.setLayoutConfig(newLayoutConfig(LayoutConfig.MATCH_PARENT,LayoutConfig.MATCH_PARENT));
addComponent(imageView,0);//将裁切框区域和选择区域添加到布局中
addComponent(selectionView,1);

if(editableImage!=null){
editableImage.setViewSize(mWidth,mHeight);
imageView.setPixelMap(editableImage.getOriginalPixelMap());
imageView.setScaleMode(Image.ScaleMode.ZOOM_CENTER);//中心缩放模式
selectionView.setBoxSize(editableImage,editableImage.getBoxes(),mWidth,mHeight);
}
}

(1)在裁切图片视图区域中适配裁切图片

由于裁切图片的视图区域与组件区域的大小相同,二裁切图片的大小是不固定的,因此裁切图片在显示到其视图区域时,需进行尺寸的适配。

上述功能是由 EditableImage 类的 getFitSize() 方法提供,该方法在上述 setBoxSize() 方法中被调用。通过该方法能够将裁切图片与其视图区域进行适配,同时返回适配后图片尺寸。

这是为了更好地在裁切图片视图区域中展示图片,无论过大或过小尺寸的图片都能在此区域中被缩放至最合适的程度显示。原理可参考图 5。

7f7d9410-417f-11ec-b939-dac502259ad0.png

图 5:适配图片尺寸原理图(左:ratio>viewRatio,右反之)

先计算原裁切图片(即粉色矩形)宽和高的比值 ratio(即 a/b)和组件区域(即黄色矩形)宽和高的比值 viewRatio(即 c/d)。

判断若原裁切图片宽高比大于裁切图片视图区域宽高比即图 5 中左图的情况,则说明可以将原裁切图片最大程度放大至宽 a 与裁切图片视图区域宽 c 长度一致的尺寸(即蓝色矩形)。

此时原裁切图片高 b 按 ratio 放大后的长度一定小于裁切图片视图区域高 d,因此可以根据图片宽放大的倍数 factor 求出放大后高的长度。

若原裁切图片宽高比小于裁切图片视图区域宽高比即图 5 中右图的情况,与上一种情况同理,则说明可以将原裁切图片最大程度放大至高 b 与裁切图片视图区域高 d 长度一致的尺寸(即蓝色矩形)。

此时原裁切图片宽 a 按 ratio 放大后的长度一定小于裁切图片视图区域宽 c,因此可以根据图片高放大的倍数 factor 求出放大后宽的长度。

计算完成后将图片放大后的宽和高分别存放在 int 型数组 fitSize[] 中。上述是以原裁切图片尺寸小于裁切图片视图区域为例,反之同理。

publicint[]getFitSize(){//适配图片,将图片缩放至比例与裁切图片视图区域比例一致
int[]fitSize=newint[2];//用于存放适配后的图片宽高
//原裁剪图片宽高比
floatratio=originalPixelMap.getImageInfo().size.width/(float)originalPixelMap.getImageInfo().size.height;
floatviewRatio=viewWidth/(float)viewHeight;//裁切图片视图区域宽高比

//原裁剪图片宽和高比例大于裁切图片视图区域宽和高比例
if(ratio>viewRatio){
floatfactor=viewWidth/(float)originalPixelMap.getImageInfo().size.width;//裁切图片宽放大的倍数
fitSize[0]=viewWidth;//宽为裁切图片视图区域宽
fitSize[1]=(int)(originalPixelMap.getImageInfo().size.height*factor);//根据宽放大的倍数计算放大后高的长度
}else{//原裁剪图片宽和高比例小于裁切图片视图区域宽和高比例
floatfactor=viewHeight/(float)originalPixelMap.getImageInfo().size.height;
fitSize[0]=(int)(originalPixelMap.getImageInfo().size.width*factor);
fitSize[1]=viewHeight;
}
returnfitSize;
}

(2)将裁切框和图片进行适配

裁切框需要与裁切图片保持相同的显示比例,因此裁切框需要和裁切图片进行适配。

上述功能是由 SelectionView 类的 setBoxsize() 方法。获取适配后图片的宽高,与裁切框宽高进行计算得到 originX 和 originY,并调用 setDisplayBoxes() 方法设置适配后裁切框的坐标。

publicvoidsetBoxSize(EditableImageeditableImage,ListoriginalBoxes,intwidthX,intheightY){
int[]fitSize=editableImage.getFitSize();//获取前面计算地适配后的图片尺寸
this.pixelMapWidth=fitSize[0];//适配后图片的宽
this.pixelMapHeight=fitSize[1];//适配后图片地高
intoriginX=(widthX-pixelMapWidth)/2;
intoriginY=(heightY-pixelMapHeight)/2;
this.originX=originX;
this.originY=originY;
setDisplayBoxes(originalBoxes);//设置适配后裁切框的坐标
invalidate();
}

setDisplayBoxes() 方法中核心部分是根据图片缩放比例计算适配后裁切框对角线上两点的坐标。

先计算图片缩放前后宽的比值 scale(即 c/a),用之前实例化时设置的裁切框初始尺寸的左上角横坐标 X1 与缩放比例 scale 相乘得到适配后的横坐标。

再加上前面计算好的 originX,即得到适配后的裁切框左上角横坐标 scaleX1,右下角横坐标 scaleX2、左上角竖坐标 scaleY1、右下角竖坐标 scaleY2 同理。

floatscale=((float)editableImage.getFitSize()[0])/editableImage.getActualSize()[0];
intscaleX1=(int)Math.ceil((originalBox.getX1()*scale)+originX);
intscaleX2=(int)Math.ceil((originalBox.getX2()*scale)+originX);
intscaleY1=(int)Math.ceil((originalBox.getY1()*scale)+originY);
intscaleY2=(int)Math.ceil((originalBox.getY2()*scale)+originY);

//将适配后的裁切框重新加入到裁切图片中
displayBox.setX1(scaleX1);
displayBox.setX2(scaleX2);
displayBox.setY1(scaleY1);
displayBox.setY2(scaleY2);
责任编辑:haq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 操作系统
    +关注

    关注

    37

    文章

    6747

    浏览量

    123207
  • 鸿蒙系统
    +关注

    关注

    183

    文章

    2634

    浏览量

    66230
  • HarmonyOS
    +关注

    关注

    79

    文章

    1968

    浏览量

    30040

原文标题:鸿蒙图片裁切组件,已开源!

文章出处:【微信号:gh_834c4b3d87fe,微信公众号:OpenHarmony技术社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    欢创播报 华为宣布鸿蒙内核超越Linux内核

    1 华为宣布鸿蒙内核超越Linux内核   6月21日,在华为开发者大会上, HarmonyOS NEXT(鸿蒙NEXT)——真正独立于
    的头像 发表于 06-27 11:30 792次阅读

    鸿蒙ArkTS声明式组件:ImageAnimator

    提供帧动画组件实现逐帧播放图片的能力,可以配置需要播放的图片列表,每张图片可以配置时长。
    的头像 发表于 06-24 11:18 482次阅读
    <b class='flag-5'>鸿蒙</b>ArkTS声明式<b class='flag-5'>组件</b>:ImageAnimator

    鸿蒙ArkTS声明式开发:跨平台支持列表【分布式迁移标识】 通用属性

    组件的分布式迁移标识,指明了该组件在分布式迁移场景下可以将特定状态恢复到对端设备。
    的头像 发表于 06-07 21:15 368次阅读

    MobTech袤博科技旗下核心SDK产品全面适配HarmonyOS NEXT鸿蒙星河版

    HarmonyOS NEXT鸿蒙星河版。 不同于系统和IOS系统,鸿蒙系统(HarmonyOS)是由华为公司推出的全场景、全平台的分布式
    的头像 发表于 05-14 17:41 445次阅读

    最新开源代码证实!“鸿蒙原生版”微信正在积极开发中

    立刻保存,不需要同步或刷新调用。共享内存一般用于后台开发。 HarmonyOS NEXT 也就是鸿蒙星河版——俗称“纯血鸿蒙”,其采用华为自研内核,抛弃了传统的 Linux 内核以及 AOSP
    发表于 05-08 17:08

    华为鸿蒙操作系统发展战略:2024年完成安应用全面迁移

    徐直军指出,2024 年,鸿蒙操作系统的主要任务是打造原生应用生态。目前,鸿蒙操作系统已基本适配各种终端设备,但在应用开发方面仍依赖生态。
    的头像 发表于 04-18 15:38 758次阅读

    鸿蒙OS开发实例:【组件模式】

    组件一直是移动端比较流行的开发方式,有着编译运行快,业务逻辑分明,任务划分清晰等优点,针对Android端的组件;与Android端的组件
    的头像 发表于 04-07 17:44 598次阅读
    <b class='flag-5'>鸿蒙</b>OS开发实例:【<b class='flag-5'>组件</b><b class='flag-5'>化</b>模式】

    面对如今的就业环境,vs鸿蒙……

    对于鸿蒙如今的发展势头,可谓是如火如荼。无论是最近发布的鸿蒙操作系统HarmonyOS NEXT,摒弃了Linux内核和AOSP开放源代码项目的代码, 不再兼容
    的头像 发表于 03-01 15:49 552次阅读
    面对如今的就业环境,<b class='flag-5'>安</b><b class='flag-5'>卓</b>vs<b class='flag-5'>鸿蒙</b>……

    没有“成份“的鸿蒙还能行吗?

    属于自己的手机操作系统。在早期鸿蒙诞生之初,鸿蒙是兼容的。有很多网友就此认定鸿蒙是“套壳
    的头像 发表于 02-26 13:56 479次阅读
    没有“<b class='flag-5'>安</b><b class='flag-5'>卓</b>成份“的<b class='flag-5'>鸿蒙</b>还能行吗?

    纯血鸿蒙系统,拿什么与、iOS比?

    ArkUI …… 2、鸿蒙进阶 Stage模型 网络、数据管理 一次开发多段部署 …… 3、鸿蒙多媒体技术 音频 视频 相机 图片 …… 4、鸿蒙南向与驱动开发 物联网 驱动开发 ……
    发表于 02-21 21:04

    华为鸿蒙系统怎么样 华为鸿蒙系统和系统的区别

    和稳定性。与此同时,鸿蒙系统与系统在架构、功能和生态系统方面都存在一些区别和特点,下面将详细探讨这些方面的差异。 首先,华为鸿蒙系统在架构上与
    的头像 发表于 02-02 14:54 1647次阅读

    鸿蒙OS和开源鸿蒙什么关系?

    内核,其他功能都以模块的形式存在。     华为用的是鸿蒙OS 我们都知道,华为手机的鸿蒙OS是可以运行软件的,是因为系统中有
    的头像 发表于 01-30 15:44 1092次阅读
    <b class='flag-5'>鸿蒙</b>OS和开源<b class='flag-5'>鸿蒙</b>什么关系?

    鸿蒙系统和的区别 鸿蒙系统有什么特别之处

    了分布式架构,可以在不同设备上实现无缝连接和协同工作。而系统采用的是集中式架构,设备之间的连接和协同工作相对较为困难。 鸿蒙系统具备高度灵活性和可扩展性,支持设备与设备之间的直接通
    的头像 发表于 01-18 11:45 1.2w次阅读

    鸿蒙开发OpenHarmony组件复用案例

    ; ...... } 开发实践 我们看下组件复用的实际使用案例。示例中,会创建一个图片列表页面,使用懒加载LazyForEach,以及组件复用能力。 创建数据源 首先,创建了一个业务对象类MyImage
    发表于 01-15 17:37

    鸿蒙系统和的区别哪个好用

    的一些问题,如性能、隐私安全等。而系统是由谷歌开发和推广的移动设备操作系统,目前在全球范围内占据主导地位。 鸿蒙系统的架构更为先进和高效。鸿蒙系统采用了分布式架构,可以在不同设备之
    的头像 发表于 01-11 11:15 1885次阅读