鸿蒙HarmonyOS App开发造轮子-自定义圆形图片组件

想了解更多内容,鸿蒙请访问:

和华为官方合作共建的造轮自定鸿蒙技术社区

https://harmonyos.51cto.com/#zz 

一、背景

在采用Java配合xml布局编写鸿蒙app页面的义圆时候,发现sdk自带的形图Image组件并不能将图片设置成圆形,反复了翻阅了官方API手册(主要查阅了Compont和Image相关的片组API),起初发现了一个setCornerRadius方法,鸿蒙于是造轮自定想着将图片宽度和高度设置为一样,然后调用该方法将radios设置为宽度或者高度的义圆一半,以为可以实现圆形图片的形图效果,后来发现不行。片组于是鸿蒙乎想着能不能通过继承原有的Image自己来动手重新自定义一个支持圆形的图片组件。

二、造轮自定思路

1、义圆对比之前自己在其他程序开发中自定义组件的形图思路,首先寻找父组件Image和Component相关的片组Api,看看是否具备OnDraw方法。

2、了解Canvas相关Api操作,特别是涉及到位图的操作。服务器托管

通过翻阅大量资料,发现了两个关键的api,分别是Component的addDrawTask方法和其内部静态接口DrawTask

三、自定义组件模块

1、新建一个工程之后,创建一个独立的Java FA模块,然后删除掉里面所有布局以及自动生成的java代码,然后自己创建一个class继承ImageView;

2、写一个类继承ImageView,在其中暴露出public的设置圆形图片的api方法以供后面调用;

3、在原有的Image组件获取到位图之后,利用该位图数据利用addDrawTask方法配合Canvas进行位图输出形状的重新绘制,这里需要使用Canvas的一个关键api方法drawPixelMapHolderRoundRectShape;

4、注意,为了让Canvas最后输出的图片为圆形,需要将图片在布局中的宽度和高度设置成一样,否则输出的为圆角矩形或者椭圆形。

最后封装后的详细代码如下:

package com.xdw.customview; import ohos.agp.components.AttrSet; import ohos.agp.components.Image; import ohos.agp.render.PixelMapHolder; import ohos.agp.utils.RectFloat; import ohos.app.Context; import ohos.hiviewdfx.HiLog; import ohos.hiviewdfx.HiLogLabel; import ohos.media.image.ImageSource; import ohos.media.image.PixelMap; import ohos.media.image.common.PixelFormat; import ohos.media.image.common.Rect; import ohos.media.image.common.Size; import java.io.InputStream; /**  * Created by 夏德旺 on 2021/1/1 11:00  */ public class RoundImage extends Image {      private static final HiLogLabel LABEL = new HiLogLabel(HiLog.DEBUG, 0, "RoundImage");     private PixelMapHolder pixelMapHolder;//像素图片持有者     private RectFloat rectDst;//目标区域     private RectFloat rectSrc;//源区域     public RoundImage(Context context) {          this(context,null);     }     public RoundImage(Context context, AttrSet attrSet) {          this(context,attrSet,null);     }     /**      * 加载包含该控件的xml布局,亿华云会执行该构造函数      * @param context      * @param attrSet      * @param styleName      */     public RoundImage(Context context, AttrSet attrSet, String styleName) {          super(context, attrSet, styleName);         HiLog.error(LABEL,"RoundImage");     }     public void onRoundRectDraw(int radius){          //添加绘制任务         this.addDrawTask((view, canvas) -> {              if (pixelMapHolder == null){                  return;             }             synchronized (pixelMapHolder) {                  //给目标区域赋值,宽度和高度取自xml配置文件中的属性                 rectDst = new RectFloat(0,0,getWidth(),getHeight());                 //绘制圆角图片                 canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, radius, radius);                 pixelMapHolder = null;             }         });     }     //使用canvas绘制圆形     private void onCircleDraw(){          //添加绘制任务,自定义组件的核心api调用,该接口的参数为Component下的DrawTask接口         this.addDrawTask((view, canvas) -> {              if (pixelMapHolder == null){                  return;             }             synchronized (pixelMapHolder) {                  //给目标区域赋值,宽度和高度取自xml配置文件中的属性                 rectDst = new RectFloat(0,0,getWidth(),getHeight());                 //使用canvas绘制输出圆角矩形的位图,该方法第4个参数和第5个参数为radios参数,                 // 绘制图片,必须把图片的宽度和高度先设置成一样,然后把它们设置为图片宽度或者高度一半时则绘制的为圆形                 canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, getWidth()/2, getHeight()/2);                 pixelMapHolder = null;             }         });     }     /**      *获取原有Image中的位图资源后重新检验绘制该组件      * @param pixelMap      */     private void putPixelMap(PixelMap pixelMap){          if (pixelMap != null) {              rectSrc = new RectFloat(0, 0, pixelMap.getImageInfo().size.width, pixelMap.getImageInfo().size.height);             pixelMapHolder = new PixelMapHolder(pixelMap);             invalidate();//重新检验该组件         }else{              pixelMapHolder = null;             setPixelMap(null);         }     }     /**      * 通过资源ID获取位图对象      **/     private PixelMap getPixelMap(int resId) {          InputStream drawableInputStream = null;         try {              drawableInputStream = getResourceManager().getResource(resId);             ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();             sourceOptions.formatHint = "image/png";             ImageSource imageSource = ImageSource.create(drawableInputStream, null);             ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();             decodingOptions.desiredSize = new Size(0, 0);             decodingOptions.desiredRegion = new Rect(0, 0, 0, 0);             decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;             PixelMap pixelMap = imageSource.createPixelmap(decodingOptions);             return pixelMap;         } catch (Exception e) {              e.printStackTrace();         } finally {              try{                  if (drawableInputStream != null){                      drawableInputStream.close();                 }             }catch (Exception e) {                  e.printStackTrace();             }         }         return null;     }     /**      * 对外调用的api,设置圆形图片方法      * @param resId      */     public void setPixelMapAndCircle(int resId){          PixelMap pixelMap = getPixelMap(resId);         putPixelMap(pixelMap);         onCircleDraw();     }     /**      * 对外调用的api,设置圆角图片方法      * @param resId      * @param radius      */     public void setPixelMapAndRoundRect(int resId,int radius){          PixelMap pixelMap = getPixelMap(resId);         putPixelMap(pixelMap);         onRoundRectDraw(radius);     } } 

 5、修改config.json文件,代码如下:

{    "app": {      "bundleName": "com.xdw.customview",     "vendor": "xdw",     "version": {        "code": 1,       "name": "1.0"     },     "apiVersion": {        "compatible": 4,       "target": 4,       "releaseType": "Beta1"     }   },   "deviceConfig": { },   "module": {      "package": "com.xdw.customview",     "deviceType": [       "phone",       "tv",       "tablet",       "car",       "wearable"     ],     "reqPermissions": [       {          "name": "ohos.permission.INTERNET"       }     ],     "distro": {        "deliveryWithInstall": true,       "moduleName": "roundimage",       "moduleType": "har"     }   } } 

 这样该模块就可以导出后续给其他所有工程引用了,后面还可以编译之后发布到gradle上直接通过添加依赖来进行使用(这个是后话),下面我们先通过本地依赖导入的方式来调用这个自定义组件模块吧。

四、其他工程调用该自定义组件并测试效果

1、再来新建一个工程,然后将之前的模块导入到新建的云服务器提供商工程中(DevEco暂时不支持自动导入外部模块的操作,需要手动导入操作,请关注我的另外一篇博客)。

2、在gradle中引用导入的模块的组件,代码如下:

dependencies {      entryImplementation project(:entry)     implementation fileTree(dir: libs, include: [*.jar, *.har])     testCompilejunit:junit:4.12 } 

 3、在布局中引用自定义的圆形图片,代码如下:

<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout     xmlns:ohos="http://schemas.huawei.com/res/ohos"     ohos:height="match_parent"     ohos:width="match_parent"     ohos:orientation="vertical">      <Text         ohos:id="$+id:text_helloworld"         ohos:height="match_content"         ohos:width="match_content"         ohos:background_element="$graphic:background_ability_main"         ohos:layout_alignment="horizontal_center"         ohos:text="Hello World"         ohos:text_size="50"         />      <com.xdw.customview.RoundImage         ohos:id="$+id:image"         ohos:height="200vp"         ohos:width="200vp"/> </DirectionalLayout> 

 4、在Java代码中进行调用,代码如下:

package com.example.testcustomview.slice;  import com.example.testcustomview.ResourceTable; import com.xdw.customview.RoundImage; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.content.Intent;  public class MainAbilitySlice extends AbilitySlice {      @Override     public void onStart(Intent intent) {          super.onStart(intent);         super.setUIContent(ResourceTable.Layout_ability_main);         RoundImage roundImage = (RoundImage) findComponentById(ResourceTable.Id_image);         roundImage.setPixelMapAndCircle(ResourceTable.Media_man);     }      @Override     public void onActive() {          super.onActive();     }      @Override     public void onForeground(Intent intent) {          super.onForeground(intent);     } } 

 5、开启手机模拟器进行测试,效果如下:

©著作权归作者和HarmonyOS技术社区共同所有,如需转载,请注明出处,否则将追究法律责任。

想了解更多内容,请访问:

和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

-->
滇ICP备2023000592号-31