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

开发 OpenHarmony
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com/#zz

[[375008]]

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

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

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最后输出的图片为圆形,需要将图片在布局中的宽度和高度设置成一样,否则输出的为圆角矩形或者椭圆形。

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

  1. package com.xdw.customview; 
  2.  
  3. import ohos.agp.components.AttrSet; 
  4. import ohos.agp.components.Image; 
  5. import ohos.agp.render.PixelMapHolder; 
  6. import ohos.agp.utils.RectFloat; 
  7. import ohos.app.Context; 
  8. import ohos.hiviewdfx.HiLog; 
  9. import ohos.hiviewdfx.HiLogLabel; 
  10. import ohos.media.image.ImageSource; 
  11. import ohos.media.image.PixelMap; 
  12. import ohos.media.image.common.PixelFormat; 
  13. import ohos.media.image.common.Rect; 
  14. import ohos.media.image.common.Size
  15.  
  16. import java.io.InputStream; 
  17.  
  18. /** 
  19.  * Created by 夏德旺 on 2021/1/1 11:00 
  20.  */ 
  21. public class RoundImage extends Image { 
  22.     private static final HiLogLabel LABEL = new HiLogLabel(HiLog.DEBUG, 0, "RoundImage"); 
  23.     private PixelMapHolder pixelMapHolder;//像素图片持有者 
  24.     private RectFloat rectDst;//目标区域 
  25.     private RectFloat rectSrc;//源区域 
  26.     public RoundImage(Context context) { 
  27.         this(context,null); 
  28.  
  29.     } 
  30.  
  31.     public RoundImage(Context context, AttrSet attrSet) { 
  32.         this(context,attrSet,null); 
  33.     } 
  34.  
  35.     /** 
  36.      * 加载包含该控件的xml布局,会执行该构造函数 
  37.      * @param context 
  38.      * @param attrSet 
  39.      * @param styleName 
  40.      */ 
  41.     public RoundImage(Context context, AttrSet attrSet, String styleName) { 
  42.         super(context, attrSet, styleName); 
  43.         HiLog.error(LABEL,"RoundImage"); 
  44.     } 
  45.  
  46.  
  47.  
  48.     public void onRoundRectDraw(int radius){ 
  49.         //添加绘制任务 
  50.         this.addDrawTask((view, canvas) -> { 
  51.             if (pixelMapHolder == null){ 
  52.                 return
  53.             } 
  54.             synchronized (pixelMapHolder) { 
  55.                 //给目标区域赋值,宽度和高度取自xml配置文件中的属性 
  56.                 rectDst = new RectFloat(0,0,getWidth(),getHeight()); 
  57.                 //绘制圆角图片 
  58.                 canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, radius, radius); 
  59.                 pixelMapHolder = null
  60.             } 
  61.         }); 
  62.     } 
  63.  
  64.     //使用canvas绘制圆形 
  65.     private void onCircleDraw(){ 
  66.         //添加绘制任务,自定义组件的核心api调用,该接口的参数为Component下的DrawTask接口 
  67.         this.addDrawTask((view, canvas) -> { 
  68.             if (pixelMapHolder == null){ 
  69.                 return
  70.             } 
  71.             synchronized (pixelMapHolder) { 
  72.                 //给目标区域赋值,宽度和高度取自xml配置文件中的属性 
  73.                 rectDst = new RectFloat(0,0,getWidth(),getHeight()); 
  74.                 //使用canvas绘制输出圆角矩形的位图,该方法第4个参数和第5个参数为radios参数, 
  75.                 // 绘制图片,必须把图片的宽度和高度先设置成一样,然后把它们设置为图片宽度或者高度一半时则绘制的为圆形 
  76.                 canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, getWidth()/2, getHeight()/2); 
  77.                 pixelMapHolder = null
  78.             } 
  79.         }); 
  80.     } 
  81.  
  82.  
  83.     /** 
  84.      *获取原有Image中的位图资源后重新检验绘制该组件 
  85.      * @param pixelMap 
  86.      */ 
  87.     private void putPixelMap(PixelMap pixelMap){ 
  88.         if (pixelMap != null) { 
  89.             rectSrc = new RectFloat(0, 0, pixelMap.getImageInfo().size.width, pixelMap.getImageInfo().size.height); 
  90.             pixelMapHolder = new PixelMapHolder(pixelMap); 
  91.             invalidate();//重新检验该组件 
  92.         }else
  93.             pixelMapHolder = null
  94.             setPixelMap(null); 
  95.         } 
  96.     } 
  97.  
  98.  
  99.     /** 
  100.      * 通过资源ID获取位图对象 
  101.      **/ 
  102.     private PixelMap getPixelMap(int resId) { 
  103.         InputStream drawableInputStream = null
  104.         try { 
  105.             drawableInputStream = getResourceManager().getResource(resId); 
  106.             ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions(); 
  107.             sourceOptions.formatHint = "image/png"
  108.             ImageSource imageSource = ImageSource.create(drawableInputStream, null); 
  109.             ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions(); 
  110.             decodingOptions.desiredSize = new Size(0, 0); 
  111.             decodingOptions.desiredRegion = new Rect(0, 0, 0, 0); 
  112.             decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888; 
  113.             PixelMap pixelMap = imageSource.createPixelmap(decodingOptions); 
  114.             return pixelMap; 
  115.         } catch (Exception e) { 
  116.             e.printStackTrace(); 
  117.         } finally { 
  118.             try{ 
  119.                 if (drawableInputStream != null){ 
  120.                     drawableInputStream.close(); 
  121.                 } 
  122.             }catch (Exception e) { 
  123.                 e.printStackTrace(); 
  124.             } 
  125.         } 
  126.         return null
  127.     } 
  128.  
  129.     /** 
  130.      * 对外调用的api,设置圆形图片方法 
  131.      * @param resId 
  132.      */ 
  133.     public void setPixelMapAndCircle(int resId){ 
  134.         PixelMap pixelMap = getPixelMap(resId); 
  135.         putPixelMap(pixelMap); 
  136.         onCircleDraw(); 
  137.     } 
  138.  
  139.     /** 
  140.      * 对外调用的api,设置圆角图片方法 
  141.      * @param resId 
  142.      * @param radius 
  143.      */ 
  144.     public void setPixelMapAndRoundRect(int resId,int radius){ 
  145.         PixelMap pixelMap = getPixelMap(resId); 
  146.         putPixelMap(pixelMap); 
  147.         onRoundRectDraw(radius); 
  148.     } 

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

  1.   "app": { 
  2.     "bundleName""com.xdw.customview"
  3.     "vendor""xdw"
  4.     "version": { 
  5.       "code": 1, 
  6.       "name""1.0" 
  7.     }, 
  8.     "apiVersion": { 
  9.       "compatible": 4, 
  10.       "target": 4, 
  11.       "releaseType""Beta1" 
  12.     } 
  13.   }, 
  14.   "deviceConfig": {}, 
  15.   "module": { 
  16.     "package""com.xdw.customview"
  17.     "deviceType": [ 
  18.       "phone"
  19.       "tv"
  20.       "tablet"
  21.       "car"
  22.       "wearable" 
  23.     ], 
  24.     "reqPermissions": [ 
  25.       { 
  26.         "name""ohos.permission.INTERNET" 
  27.       } 
  28.     ], 
  29.     "distro": { 
  30.       "deliveryWithInstall"true
  31.       "moduleName""roundimage"
  32.       "moduleType""har" 
  33.     } 
  34.   } 

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

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

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

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

  1. dependencies { 
  2.     entryImplementation project(':entry'
  3.     implementation fileTree(dir: 'libs', include: ['*.jar''*.har']) 
  4.     testCompile'junit:junit:4.12' 

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

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DirectionalLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_parent" 
  5.     ohos:width="match_parent" 
  6.     ohos:orientation="vertical"
  7.  
  8.     <Text 
  9.         ohos:id="$+id:text_helloworld" 
  10.         ohos:height="match_content" 
  11.         ohos:width="match_content" 
  12.         ohos:background_element="$graphic:background_ability_main" 
  13.         ohos:layout_alignment="horizontal_center" 
  14.         ohos:text="Hello World" 
  15.         ohos:text_size="50" 
  16.         /> 
  17.  
  18.     <com.xdw.customview.RoundImage 
  19.         ohos:id="$+id:image" 
  20.         ohos:height="200vp" 
  21.         ohos:width="200vp"/> 
  22. </DirectionalLayout> 

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

  1. package com.example.testcustomview.slice; 
  2.  
  3. import com.example.testcustomview.ResourceTable; 
  4. import com.xdw.customview.RoundImage; 
  5. import ohos.aafwk.ability.AbilitySlice; 
  6. import ohos.aafwk.content.Intent; 
  7.  
  8. public class MainAbilitySlice extends AbilitySlice { 
  9.     @Override 
  10.     public void onStart(Intent intent) { 
  11.         super.onStart(intent); 
  12.         super.setUIContent(ResourceTable.Layout_ability_main); 
  13.         RoundImage roundImage = (RoundImage) findComponentById(ResourceTable.Id_image); 
  14.         roundImage.setPixelMapAndCircle(ResourceTable.Media_man); 
  15.     } 
  16.  
  17.     @Override 
  18.     public void onActive() { 
  19.         super.onActive(); 
  20.     } 
  21.  
  22.     @Override 
  23.     public void onForeground(Intent intent) { 
  24.         super.onForeground(intent); 
  25.     } 

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


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

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

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

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

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-10-26 15:54:46

canvas组件鸿蒙

2021-12-30 16:10:52

鸿蒙HarmonyOS应用

2022-04-24 15:17:56

鸿蒙操作系统

2023-02-20 15:20:43

启动页组件鸿蒙

2021-11-01 10:21:36

鸿蒙HarmonyOS应用

2022-06-30 14:02:07

鸿蒙开发消息弹窗组件

2021-09-15 10:19:15

鸿蒙HarmonyOS应用

2022-07-15 16:45:35

slider滑块组件鸿蒙

2021-02-20 12:34:53

鸿蒙HarmonyOS应用开发

2022-02-21 15:16:30

HarmonyOS鸿蒙操作系统

2022-06-20 15:43:45

switch开关鸿蒙

2021-12-21 15:22:22

鸿蒙HarmonyOS应用

2022-07-06 20:24:08

ArkUI计时组件

2022-10-25 15:12:24

自定义组件鸿蒙

2009-06-24 15:13:36

自定义JSF组件

2022-02-16 16:09:12

鸿蒙游戏操作系统

2021-12-24 15:46:23

鸿蒙HarmonyOS应用

2021-11-22 10:00:33

鸿蒙HarmonyOS应用

2022-02-16 15:25:31

JS代码Canvas鸿蒙

2022-07-12 16:56:48

自定义组件鸿蒙
点赞
收藏

51CTO技术栈公众号