鴻蒙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'])
- 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技術社區共同所有,如需轉載,請注明出處,否則將追究法律責任。
https://harmonyos.51cto.com/#zz