一、背景
在采用Java配合xml布局编写鸿蒙app页面的时候,发现sdk自带的Image组件并不能将图片设置成圆形,反复了翻阅了官方API手册(主要查阅了Compont和Image相关的API),起初发现了一个setCornerRadius方法,于是想着将图片宽度和高度设置为一样,然后调用该方法将radios设置为宽度或者高度的一半,以为可以实现圆形图片的效果,后来发现不行。于是乎想着能不能通过继承原有的Image自己来动手重新自定义一个支持圆形的图片组件。
二、思路
对比之前自己在其他程序开发中自定义组件的思路,首先寻找父组件Image和Component相关的Api,看看是否具备OnDraw方法。
了解Canvas相关Api操作,特别是涉及到位图的操作。
通过翻阅大量资料,发现了两个关键的api,分别是Component的addDrawTask方法和其内部静态接口DrawTask
三、自定义组件模块
新建一个工程之后,创建一个独立的Java FA模块,然后删除掉里面所有布局以及自动生成的java代码,然后自己创建一个class继承ImageView;
写一个类继承ImageView,在其中暴露出public的设置圆形图片的api方法以供后面调用;
在原有的Image组件获取到位图之后,利用该位图数据利用addDrawTask方法配合Canvas进行位图输出形状的重新绘制,这里需要使用Canvas的一个关键api方法drawPixelMapHolderRoundRectShape;
注意,为了让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;
-
-
- 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);
- }
-
-
- 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;
- }
- });
- }
-
-
-
- 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);
- }
- }
-
-
-
- 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;
- }
-
-
- public void setPixelMapAndCircle(int resId){
- PixelMap pixelMap = getPixelMap(resId);
- putPixelMap(pixelMap);
- onCircleDraw();
- }
-
-
- 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上直接通过添加依赖来进行使用(这个是后话),下面我们先通过本地依赖导入的方式来调用这个自定义组件模块吧。
四、其他工程调用该自定义组件并测试效果
再来新建一个工程,然后将之前的模块导入到新建的工程中(DevEco暂时不支持自动导入外部模块的操作,需要手动导入操作,请关注我的另外一篇博客)。
在gradle中引用导入的模块的组件,代码如下:
- dependencies {
- entryImplementation project(':entry')
- implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
- testCompile'junit:junit:4.12'
- }
3、在布局中引用自定义的圆形图片,代码如下:
- "1.0" encoding="utf-8"?>
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:orientation="vertical">
-
-
- 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"
- />
-
-
- ohos:id="$+id:image"
- ohos:height="200vp"
- ohos:width="200vp"/>
-
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技术社区共同所有,如需转载,请注明出处,否则将追究法律责任。