鴻蒙HarmonyOS App開(kāi)發(fā)造輪子-自定義圓形圖片組件
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
一、背景
在采用Java配合xml布局編寫鴻蒙app頁(yè)面的時(shí)候,發(fā)現(xiàn)sdk自帶的Image組件并不能將圖片設(shè)置成圓形,反復(fù)了翻閱了官方API手冊(cè)(主要查閱了Compont和Image相關(guān)的API),起初發(fā)現(xiàn)了一個(gè)setCornerRadius方法,于是想著將圖片寬度和高度設(shè)置為一樣,然后調(diào)用該方法將radios設(shè)置為寬度或者高度的一半,以為可以實(shí)現(xiàn)圓形圖片的效果,后來(lái)發(fā)現(xiàn)不行。于是乎想著能不能通過(guò)繼承原有的Image自己來(lái)動(dòng)手重新自定義一個(gè)支持圓形的圖片組件。
二、思路
1、對(duì)比之前自己在其他程序開(kāi)發(fā)中自定義組件的思路,首先尋找父組件Image和Component相關(guān)的Api,看看是否具備OnDraw方法。
2、了解Canvas相關(guān)Api操作,特別是涉及到位圖的操作。
通過(guò)翻閱大量資料,發(fā)現(xiàn)了兩個(gè)關(guān)鍵的api,分別是Component的addDrawTask方法和其內(nèi)部靜態(tài)接口DrawTask


三、自定義組件模塊
1、新建一個(gè)工程之后,創(chuàng)建一個(gè)獨(dú)立的Java FA模塊,然后刪除掉里面所有布局以及自動(dòng)生成的java代碼,然后自己創(chuàng)建一個(gè)class繼承ImageView;
2、寫一個(gè)類繼承ImageView,在其中暴露出public的設(shè)置圓形圖片的api方法以供后面調(diào)用;
3、在原有的Image組件獲取到位圖之后,利用該位圖數(shù)據(jù)利用addDrawTask方法配合Canvas進(jìn)行位圖輸出形狀的重新繪制,這里需要使用Canvas的一個(gè)關(guān)鍵api方法drawPixelMapHolderRoundRectShape;
4、注意,為了讓Canvas最后輸出的圖片為圓形,需要將圖片在布局中的寬度和高度設(shè)置成一樣,否則輸出的為圓角矩形或者橢圓形。
最后封裝后的詳細(xì)代碼如下:
- 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;//目標(biāo)區(qū)域
 - private RectFloat rectSrc;//源區(qū)域
 - public RoundImage(Context context) {
 - this(context,null);
 - }
 - public RoundImage(Context context, AttrSet attrSet) {
 - this(context,attrSet,null);
 - }
 - /**
 - * 加載包含該控件的xml布局,會(huì)執(zhí)行該構(gòu)造函數(shù)
 - * @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){
 - //添加繪制任務(wù)
 - this.addDrawTask((view, canvas) -> {
 - if (pixelMapHolder == null){
 - return;
 - }
 - synchronized (pixelMapHolder) {
 - //給目標(biāo)區(qū)域賦值,寬度和高度取自xml配置文件中的屬性
 - rectDst = new RectFloat(0,0,getWidth(),getHeight());
 - //繪制圓角圖片
 - canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, radius, radius);
 - pixelMapHolder = null;
 - }
 - });
 - }
 - //使用canvas繪制圓形
 - private void onCircleDraw(){
 - //添加繪制任務(wù),自定義組件的核心api調(diào)用,該接口的參數(shù)為Component下的DrawTask接口
 - this.addDrawTask((view, canvas) -> {
 - if (pixelMapHolder == null){
 - return;
 - }
 - synchronized (pixelMapHolder) {
 - //給目標(biāo)區(qū)域賦值,寬度和高度取自xml配置文件中的屬性
 - rectDst = new RectFloat(0,0,getWidth(),getHeight());
 - //使用canvas繪制輸出圓角矩形的位圖,該方法第4個(gè)參數(shù)和第5個(gè)參數(shù)為radios參數(shù),
 - // 繪制圖片,必須把圖片的寬度和高度先設(shè)置成一樣,然后把它們?cè)O(shè)置為圖片寬度或者高度一半時(shí)則繪制的為圓形
 - canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, getWidth()/2, getHeight()/2);
 - pixelMapHolder = null;
 - }
 - });
 - }
 - /**
 - *獲取原有Image中的位圖資源后重新檢驗(yàn)繪制該組件
 - * @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();//重新檢驗(yàn)該組件
 - }else{
 - pixelMapHolder = null;
 - setPixelMap(null);
 - }
 - }
 - /**
 - * 通過(guò)資源ID獲取位圖對(duì)象
 - **/
 - 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;
 - }
 - /**
 - * 對(duì)外調(diào)用的api,設(shè)置圓形圖片方法
 - * @param resId
 - */
 - public void setPixelMapAndCircle(int resId){
 - PixelMap pixelMap = getPixelMap(resId);
 - putPixelMap(pixelMap);
 - onCircleDraw();
 - }
 - /**
 - * 對(duì)外調(diào)用的api,設(shè)置圓角圖片方法
 - * @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"
 - }
 - }
 - }
 
這樣該模塊就可以導(dǎo)出后續(xù)給其他所有工程引用了,后面還可以編譯之后發(fā)布到gradle上直接通過(guò)添加依賴來(lái)進(jìn)行使用(這個(gè)是后話),下面我們先通過(guò)本地依賴導(dǎo)入的方式來(lái)調(diào)用這個(gè)自定義組件模塊吧。
四、其他工程調(diào)用該自定義組件并測(cè)試效果
1、再來(lái)新建一個(gè)工程,然后將之前的模塊導(dǎo)入到新建的工程中(DevEco暫時(shí)不支持自動(dòng)導(dǎo)入外部模塊的操作,需要手動(dòng)導(dǎo)入操作,請(qǐng)關(guān)注我的另外一篇博客)。
2、在gradle中引用導(dǎo)入的模塊的組件,代碼如下:
- 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代碼中進(jìn)行調(diào)用,代碼如下:
- 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、開(kāi)啟手機(jī)模擬器進(jìn)行測(cè)試,效果如下:

©著作權(quán)歸作者和HarmonyOS技術(shù)社區(qū)共同所有,如需轉(zhuǎn)載,請(qǐng)注明出處,否則將追究法律責(zé)任。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
















 
 
 










 
 
 
 