在采用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'])
testCompile'junit: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 NEXT)资料用来跟着学习是非常有必要的。
点击领取→【纯血版鸿蒙全套最新学习资料】(安全链接,放心点击)希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
鸿蒙(HarmonyOS NEXT)最新学习路线
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
HarmonyOS Next 最新全套视频教程
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
- ArkTS语言
- 安装DevEco Studio
- 运用你的第一个ArkTS应用
- ArkUI声明式UI开发
- .……
《鸿蒙开发进阶》
- Stage模型入门
- 网络管理
- 数据管理
- 电话服务
- 分布式应用开发
- 通知与窗口管理
- 多媒体技术
- 安全技能
- 任务管理
- WebGL
- 国际化开发
- 应用测试
- DFX面向未来设计
- 鸿蒙系统移植和裁剪定制
- ……
《鸿蒙进阶实战》
- ArkTS实践
- UIAbility应用
- 网络案例
- ……
大厂面试必问面试题
鸿蒙南向开发技术
鸿蒙APP开发必备
鸿蒙生态应用开发白皮书V2.0PDF
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。