HarmonyOS自定義組件之圖層的使用
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
HarmonyOS目前已經(jīng)有較豐富的自定義組件了,但是多數(shù)自定義組件都沒有講到圖層這個(gè)概念,都是使用默認(rèn)圖層進(jìn)行繪制。這里就通過幾個(gè)小例子介紹一下HarmonyOS自定義組件中圖層概念和使用。
使用說明
1.HarmonyOS的繪制入口
1.1 DrawTask接口
HarmonyOS的Component組件對(duì)外提供了一個(gè)DrawTask接口,通過addDrawTask方法為組件添加一個(gè)DrawTask,讓開發(fā)者可以進(jìn)行自定義繪制邏輯。首先我們看下DrawTask的描述:
- public interface DrawTask {
 - int BETWEEN_BACKGROUND_AND_CONTENT = 1;
 - int BETWEEN_CONTENT_AND_FOREGROUND = 2;
 - void onDraw(Component component, Canvas canvas);
 - }
 
1.2 onDraw繪制方法
這個(gè)接口中只有一個(gè)onDraw方法,做移動(dòng)開發(fā)的同學(xué)對(duì)這個(gè)名稱的方法應(yīng)該都很熟悉,在這個(gè)回調(diào)里系統(tǒng)提供了一塊canvas畫布,我們可以調(diào)用canvas的api進(jìn)行一些基礎(chǔ)圖形的組合繪制。
2.HarmonyOS圖層介紹
2.1 圖層概念介紹
HarmonyOS為每個(gè)組件定義了5個(gè)圖層,從下往上分別為:Background -> UserLayer1 -> Content -> UserLayer2 -> Foreground,并且繪制流程也是按照從下往上的順序進(jìn)行繪制的,這點(diǎn)其實(shí)和我們熟悉的View的繪制流程基本一致。這中設(shè)計(jì)和PS中的圖層概念基本一致,通過構(gòu)建多個(gè)圖層分別進(jìn)行操作,最終再合成為一個(gè)圖層渲染到屏幕上。我們可以用下面這圖來理解:

在這個(gè)圖層結(jié)構(gòu)中,有兩個(gè)layer是提供給上層應(yīng)用使用的,分別如圖中的UserLayer1和UserLayer2。他們分別位于背景與內(nèi)容之間,內(nèi)容與前景之間。其中,背景,內(nèi)容,前景為系統(tǒng)私有,應(yīng)用層無法對(duì)其進(jìn)行干預(yù)。(這里補(bǔ)充說明一點(diǎn),從官方API文檔說明來看是存在Foreground圖層的,但是實(shí)際中尚不清楚系統(tǒng)是如何使用該圖層,個(gè)人猜測應(yīng)該是留給列表的滾動(dòng)條這類場景使用。)
2.2 圖層概念實(shí)踐
在第一節(jié)的DrawTask接口介紹中,里面定義和圖層相關(guān)的兩個(gè)常量:BETWEEN_BACKGROUND_AND_CONTENT 和 BETWEEN_CONTENT_AND_FOREGROUND,對(duì)應(yīng)的就是上圖中的兩個(gè)User Layer。
下面我們用代碼來實(shí)踐一下這個(gè)圖層結(jié)構(gòu)和這兩個(gè)常量的作用。
自定義一個(gè)Text組件,分別在上圖中的兩個(gè)UserLayer中繪制不同顏色的實(shí)心矩形:
- public class CustomText extends Text {
 - public CustomText(Context context, AttrSet attrSet) {
 - super(context, attrSet);
 - init();
 - }
 - private void prepare() {
 - // 繪制紅色背景
 - ShapeElement background = new ShapeElement();
 - background.setShaderType(ShapeElement.RECTANGLE);
 - background.setRgbColor(new RgbColor(255,0,0));
 - setBackground(background);
 - // 繪制白色文字
 - setText("你好你好你好你好你好");
 - setTextColor(Color.WHITE);
 - }
 - private void init() {
 - prepare();
 - // 在BETWEEN_BACKGROUND_AND_CONTENT圖層上繪制藍(lán)色矩形
 - addDrawTask(new BackDrawTask(), DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
 - // 在BETWEEN_CONTENT_AND_FOREGROUND圖層上繪制綠色矩形
 - addDrawTask(new ForeDrawTask(), DrawTask.BETWEEN_CONTENT_AND_FOREGROUND);
 - }
 - // BETWEEN_BACKGROUND_AND_CONTENT
 - private static class BackDrawTask implements DrawTask {
 - @Override
 - public void onDraw(Component component, Canvas canvas) {
 - final int offset = 50;
 - Paint paint = new Paint();
 - paint.setStyle(Paint.Style.FILL_STYLE);
 - paint.setColor(Color.BLUE);
 - canvas.drawRect(new Rect(offset, offset, component.getWidth() - offset, component.getHeight() - offset), paint);
 - }
 - }
 - // BETWEEN_CONTENT_AND_FOREGROUND
 - private static class ForeDrawTask implements DrawTask {
 - @Override
 - public void onDraw(Component component, Canvas canvas) {
 - final int offset = 100;
 - Paint paint = new Paint();
 - paint.setStyle(Paint.Style.FILL_STYLE);
 - paint.setColor(Color.GREEN);
 - canvas.drawRect(new Rect(offset, offset, component.getWidth() - offset, component.getHeight() - offset), paint);
 - }
 - }
 
布局代碼如下:
- <DirectionalLayout
 - xmlns:ohos="http://schemas.huawei.com/res/ohos"
 - ohos:height="match_parent"
 - ohos:width="match_parent"
 - ohos:alignment="center"
 - ohos:orientation="vertical">
 - <com.example.myapplication.CustomText
 - ohos:height="300vp"
 - ohos:width="300vp"
 - ohos:layout_alignment="horizontal_center"
 - ohos:text_size="40vp" />
 - </DirectionalLayout>
 
運(yùn)行項(xiàng)目,我們來看下實(shí)際的渲染效果,的確符合預(yù)期。

從目視的垂直方向,從底部往上的內(nèi)容依次是:
最底層的紅色背景(Background),
自定義的藍(lán)色矩形(BackDrawTask),
Text自己的文本內(nèi)容(Content),
自定義的綠色矩形(ForeDrawTask)。
3.HarmonyOS圖層的使用
3.1 繼承Component的自定義組件
當(dāng)我們繼承Component去實(shí)現(xiàn)自定義組件時(shí),這時(shí)候不論選用哪個(gè)圖層進(jìn)行繪制都是一樣。因?yàn)閮蓚€(gè)UserLayer都是位于Background圖層之上,而默認(rèn)的Component組件并沒有內(nèi)容層和前景的默認(rèn)繪制,因此可以忽略這兩個(gè)圖層。
3.2 通過圖層實(shí)現(xiàn)水波紋效果
當(dāng)我們繼承系統(tǒng)組件,系統(tǒng)組件的內(nèi)容層和前景層都有內(nèi)部繪制邏輯,因此我們慎重選擇UserLayer圖層了。
給自定義Button添加一個(gè)水波紋的觸摸反饋效果**(水波紋效果是需要繪制在button文字之下,button背景之上,因此使用 BETWEEN_BACKGROUND_AND_CONTENT)**:
- public class CustomButton extends Button implements Component.TouchEventListener, Component.DrawTask {
 - private float downX, downY;
 - private int maxRadius;
 - private float currentRadius;
 - private final Paint paint = new Paint();
 - {
 - paint.setColor(Color.YELLOW);
 - paint.setStyle(Paint.Style.FILL_STYLE);
 - }
 - private AnimatorValue av = new AnimatorValue();
 - public CustomButton(Context context, AttrSet attrSet) {
 - super(context, attrSet);
 - av.setValueUpdateListener((animatorValue, v) -> {
 - currentRadius = maxRadius * v;
 - invalidate();
 - });
 - init();
 - }
 - @Override
 - public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
 - if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
 - if (maxRadius == 0) {
 - int w = component.getWidth();
 - int h = component.getHeight();
 - maxRadius = (int) Math.sqrt(w * w + h * h);
 - }
 - int index = touchEvent.getIndex();
 - MmiPoint pointer = touchEvent.getPointerPosition(index);
 - downX = pointer.getX();
 - downY = pointer.getY();
 - av.cancel();
 - av.start();
 - return true;
 - } else if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
 - av.cancel();
 - currentRadius = 0;
 - invalidate();
 - return true;
 - }
 - return false;
 - }
 - private void init() {
 - setTouchEventListener(this);
 - addDrawTask(this, DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
 - }
 - @Override
 - public void onDraw(Component component, Canvas canvas) {
 - canvas.drawCircle(downX, downY, currentRadius, paint);
 - }
 - }
 
效果如下:

3.3 通過圖層實(shí)現(xiàn)蒙層效果
給自定義Image增加一個(gè)顏色遮罩效果**(圖片蒙層是在圖片內(nèi)容上加一個(gè)遮罩,因此需要在圖片之上進(jìn)行蒙層繪制,因此使用 BETWEEN_CONTENT_AND_FOREGROUND)**:
- public class CustomImage extends Image implements Component.DrawTask {
 - private final Paint paint = new Paint();
 - {
 - paint.setColor(Color.YELLOW);
 - paint.setStyle(Paint.Style.FILL_STYLE);
 - paint.setAlpha(0.3f);
 - }
 - public CustomImage(Context context, AttrSet attrSet) {
 - super(context, attrSet);
 - // 圖片蒙層是在圖片內(nèi)容上加一個(gè)遮罩,因此需要在內(nèi)容層和前景層進(jìn)行蒙層繪制
 - addDrawTask(this, DrawTask.BETWEEN_CONTENT_AND_FOREGROUND);
 - }
 - @Override
 - public void onDraw(Component component, Canvas canvas) {
 - canvas.drawRect(new Rect(0, 0, component.getWidth(), component.getHeight()), paint);
 - }
 - }
 
4.效果展示
效果如下,左邊為原始Image,右邊的為添加蒙版的Image:

總結(jié)
以上就是關(guān)于鴻蒙圖層的介紹,核心內(nèi)容概括如下:
- HarmonyOS為每個(gè)組件定義了5個(gè)圖層,從下往上分別為:Background、UserLayer1、Content、UserLayer2、Foreground。
 - HarmonyOS自定義組件能夠使用的圖層有兩個(gè),分別位于Background和Content、Content和Foreground之間。
 - 靈活的選擇繪制圖層,可以實(shí)現(xiàn)特殊的UI效果,例如水波紋觸摸反饋、圖片遮罩蒙層效果等。
 
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載
https://harmonyos.51cto.com/resource/1612
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
















 
 
 












 
 
 
 