• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义绘制修改器 (DrawModifier)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @xiang-shouxing-->
5<!--Designer: @xiang-shouxing-->
6<!--Tester: @sally__-->
7<!--Adviser: @HelloCrease-->
8
9## 概述
10
11当某些组件本身的绘制内容不满足需求时,可使用组件自定义绘制功能,在原有组件基础上部分绘制、或者全部自行绘制,以达到预期效果。例如:独特的按钮形状、文字和图像混合的图标等。组件自定义绘制提供了自定义绘制修改器DrawModifier,来实现更自由的组件绘制。
12
13## 使用DrawModifier接口
14
15```ts
16declare class DrawModifier {
17
18  drawBehind?(drawContext: DrawContext): void;
19
20  drawContent?(drawContext: DrawContext): void;
21
22  drawFront?(drawContext: DrawContext): void;
23
24  drawForeground?(drawContext: DrawContext): void;
25
26  invalidate(): void;
27}
28```
29
30DrawModifier可设置前景(drawForeground)、内容前景(drawFront)、内容(drawContent)和内容背景(drawBehind)的绘制方法,开发者需要重载这些方法,并通过[Canvas](arkts-drawing-customization-on-canvas.md)的接口进行自定义绘制。自定义绘制层级图如下所示。
31
32![](figures/drawModifier.png)
33
34DrawModifier还提供主动触发重绘的方法invalidate,该接口开发者无需也无法重载,调用会触发所绑定组件的重绘。
35
36> **说明:**
37>
38> 每个DrawModifier实例只能设置到一个组件上,禁止进行重复设置。
39>
40> drawContent方法会替换组件原本的内容绘制函数。
41>
42> drawForeground方法从API version 20开始支持。
43>
44> NDK的自定义绘制能力和示例请参考[自定义绘制](./arkts-user-defined-draw.md)。
45
46## 通过drawFront、drawContent、drawBehind进行自定义绘制
47
48通过drawFront、drawContent、drawBehind接口,在内容前景、内容和内容背景三个层级上对Text组件进行了自定义绘制,从而按需改变组件的绘制效果。
49
50```ts
51// xxx.ets
52import { drawing } from '@kit.ArkGraphics2D';
53import { AnimatorResult } from '@kit.ArkUI';
54
55class MyFullDrawModifier extends DrawModifier {
56  public scaleX: number = 1;
57  public scaleY: number = 1;
58  uiContext: UIContext;
59
60  constructor(uiContext: UIContext) {
61    super();
62    this.uiContext = uiContext;
63  }
64
65  // 重载drawBehind方法,实现自定义绘制内容背景。
66  drawBehind(context: DrawContext): void {
67    const brush = new drawing.Brush();
68    brush.setColor({
69      alpha: 255,
70      red: 161,
71      green: 10,
72      blue: 33
73    });
74    context.canvas.attachBrush(brush);
75    const halfWidth = context.size.width / 2;
76    const halfHeight = context.size.height / 2;
77    context.canvas.drawRect({
78      left: this.uiContext.vp2px(halfWidth - 50 * this.scaleX),
79      top: this.uiContext.vp2px(halfHeight - 50 * this.scaleY),
80      right: this.uiContext.vp2px(halfWidth + 50 * this.scaleX),
81      bottom: this.uiContext.vp2px(halfHeight + 50 * this.scaleY)
82    });
83  }
84
85  // 重载drawContent方法,实现自定义绘制内容。
86  drawContent(context: DrawContext): void {
87    const brush = new drawing.Brush();
88    brush.setColor({
89      alpha: 255,
90      red: 23,
91      green: 169,
92      blue: 141
93    });
94    context.canvas.attachBrush(brush);
95    const halfWidth = context.size.width / 2;
96    const halfHeight = context.size.height / 2;
97    context.canvas.drawRect({
98      left: this.uiContext.vp2px(halfWidth - 30 * this.scaleX),
99      top: this.uiContext.vp2px(halfHeight - 30 * this.scaleY),
100      right: this.uiContext.vp2px(halfWidth + 30 * this.scaleX),
101      bottom: this.uiContext.vp2px(halfHeight + 30 * this.scaleY)
102    });
103  }
104
105  // 重载drawFront方法,实现自定义绘制内容前景。
106  drawFront(context: DrawContext): void {
107    const brush = new drawing.Brush();
108    brush.setColor({
109      alpha: 255,
110      red: 39,
111      green: 135,
112      blue: 217
113    });
114    context.canvas.attachBrush(brush);
115    const halfWidth = context.size.width / 2;
116    const halfHeight = context.size.height / 2;
117    const radiusScale = (this.scaleX + this.scaleY) / 2;
118    context.canvas.drawCircle(this.uiContext.vp2px(halfWidth), this.uiContext.vp2px(halfHeight), this.uiContext.vp2px(20 * radiusScale));
119  }
120}
121
122class MyFrontDrawModifier extends DrawModifier {
123  public scaleX: number = 1;
124  public scaleY: number = 1;
125  uiContext: UIContext;
126
127  constructor(uiContext: UIContext) {
128    super();
129    this.uiContext = uiContext;
130  }
131
132  // 重载drawFront方法,实现自定义绘制内容前景。
133  drawFront(context: DrawContext): void {
134    const brush = new drawing.Brush();
135    brush.setColor({
136      alpha: 255,
137      red: 39,
138      green: 135,
139      blue: 217
140    });
141    context.canvas.attachBrush(brush);
142    const halfWidth = context.size.width / 2;
143    const halfHeight = context.size.height / 2;
144    const radiusScale = (this.scaleX + this.scaleY) / 2;
145    context.canvas.drawCircle(this.uiContext.vp2px(halfWidth), this.uiContext.vp2px(halfHeight), this.uiContext.vp2px(20 * radiusScale));
146  }
147}
148
149@Entry
150@Component
151struct DrawModifierExample {
152  // 将自定义绘制前景的类实例化,传入UIContext实例。
153  private fullModifier: MyFullDrawModifier = new MyFullDrawModifier(this.getUIContext());
154  private frontModifier: MyFrontDrawModifier = new MyFrontDrawModifier(this.getUIContext());
155  private drawAnimator: AnimatorResult | undefined = undefined;
156  @State modifier: DrawModifier = new MyFrontDrawModifier(this.getUIContext());
157  private count = 0;
158
159  create() {
160    // 设置绘制动画
161    let self = this;
162    this.drawAnimator = this.getUIContext().createAnimator({
163      duration: 1000,
164      easing: 'ease',
165      delay: 0,
166      fill: 'forwards',
167      direction: 'normal',
168      iterations: 1,
169      begin: 0,
170      end: 2
171    });
172    this.drawAnimator.onFrame = (value: number) => {
173      console.log('frame value =', value);
174      const tempModifier = self.modifier as MyFullDrawModifier | MyFrontDrawModifier;
175      tempModifier.scaleX = Math.abs(value - 1);
176      tempModifier.scaleY = Math.abs(value - 1);
177      self.modifier.invalidate();
178    };
179  }
180
181  build() {
182    Column() {
183      Row() {
184        Text('Text组件绑定drawModifier')
185          .width(100)
186          .height(100)
187          .margin(10)
188          .backgroundColor(Color.Gray)
189          .onClick(() => {
190            // 修改当前绘制大小
191            const tempModifier = this.modifier as MyFullDrawModifier | MyFrontDrawModifier;
192            tempModifier.scaleX -= 0.1;
193            tempModifier.scaleY -= 0.1;
194          })
195          // 调用此接口并传入自定义绘制的类实例,即可实现自定义绘制。
196          .drawModifier(this.modifier)
197      }
198
199      Row() {
200        Button('create')
201          .width(100)
202          .height(100)
203          .margin(10)
204          .backgroundColor(0xFF2787D9)
205          .onClick(() => {
206            // 创建动画
207            this.create();
208          })
209        Button('play')
210          .width(100)
211          .height(100)
212          .margin(10)
213          .backgroundColor(0xFF2787D9)
214          .onClick(() => {
215            // 播放动画
216            if (this.drawAnimator) {
217              this.drawAnimator.play();
218            }
219          })
220        Button('changeModifier')
221          .width(100)
222          .height(100)
223          .margin(10)
224          .backgroundColor(0xFF2787D9)
225          .onClick(() => {
226            // 切换modifier
227            this.count += 1;
228            if (this.count % 2 === 1) {
229              console.log('change to full modifier');
230              this.modifier = this.fullModifier;
231            } else {
232              console.log('change to front modifier');
233              this.modifier = this.frontModifier;
234            }
235          })
236      }
237    }
238    .width('100%')
239    .height('100%')
240  }
241}
242```
243
244![drawModifier.gif](figures/drawModifier.gif)
245
246## 通过drawForeground进行自定义绘制
247
248通过drawForeground接口,在组件前景层级上对Column组件进行了自定义绘制,从而改变组件前景的绘制效果。
249
250```ts
251// xxx.ets
252import { drawing } from '@kit.ArkGraphics2D';
253
254class MyForegroundDrawModifier extends DrawModifier {
255  public scaleX: number = 3;
256  public scaleY: number = 3;
257  uiContext: UIContext;
258
259  constructor(uiContext: UIContext) {
260    super();
261    this.uiContext = uiContext;
262  }
263
264  // 重载drawForeground方法,实现自定义绘制前景。
265  drawForeground(context: DrawContext): void {
266    const brush = new drawing.Brush();
267    brush.setColor({
268      alpha: 255,
269      red: 0,
270      green: 50,
271      blue: 100
272    });
273    context.canvas.attachBrush(brush);
274    const halfWidth = context.size.width / 2;
275    const halfHeight = context.size.height / 2;
276    context.canvas.drawRect({
277      left: this.uiContext.vp2px(halfWidth - 30 * this.scaleX),
278      top: this.uiContext.vp2px(halfHeight - 30 * this.scaleY),
279      right: this.uiContext.vp2px(halfWidth + 30 * this.scaleX),
280      bottom: this.uiContext.vp2px(halfHeight + 30 * this.scaleY)
281    });
282  }
283}
284
285@Entry
286@Component
287struct DrawModifierExample {
288  // 将自定义绘制前景的类实例化,传入UIContext实例。
289  private foregroundModifier: MyForegroundDrawModifier = new MyForegroundDrawModifier(this.getUIContext());
290
291  build() {
292    Column() {
293      Text('此文本是子节点')
294        .fontSize(36)
295        .width('100%')
296        .height('100%')
297        .textAlign(TextAlign.Center)
298    }
299    .margin(50)
300    .width(280)
301    .height(300)
302    .backgroundColor(0x87CEEB)
303    // 调用此接口并传入自定义绘制前景的类实例,即可实现自定义绘制前景。
304    .drawModifier(this.foregroundModifier)
305  }
306}
307```
308![drawForeground.png](figures/drawForeground.png)