1# 自定义绘制设置
2
3当某些组件本身的绘制内容不满足需求时,可使用自定义组件绘制功能,在原有组件基础上部分绘制、或者全部自行绘制,以达到预期效果。例如:独特的按钮形状、文字和图像混合的图标等。自定义组件绘制提供了自定义绘制修改器,来实现更自由地组件绘制。
4
5>  **说明:**
6>
7>  从API Version 12开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
8
9## drawModifier
10
11drawModifier(modifier: DrawModifier | undefined)
12
13设置组件的自定义绘制修改器。
14
15**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
16
17**系统能力:** SystemCapability.ArkUI.ArkUI.Full
18
19**组件支持范围:**
20
21AlphabetIndexer、Badge、Blank、Button、CalendarPicker、Checkbox、CheckboxGroup、Circle、Column、ColumnSplit、Counter、DataPanel、DatePicker、Ellipse、Flex、FlowItem、FolderStack、FormLink、Gauge、Grid、GridCol、GridItem、GridRow、Hyperlink、Image、ImageAnimator、ImageSpan、Line、List、ListItem、ListItemGroup、LoadingProgress、Marquee、Menu、MenuItem、MenuItemGroup、NavDestination、Navigation、Navigator、NavRouter、NodeContainer、Path、PatternLock、Polygon、Polyline、Progress、QRCode、Radio、Rating、Rect、Refresh、RelativeContainer、RichEditor、Row、RowSplit、Scroll、ScrollBar、Search、Select、Shape、SideBarContainer、Slider、Stack、Stepper、StepperItem、Swiper、SymbolGlyph、TabContent、Tabs、Text、TextArea、TextClock、TextInput、TextPicker、TextTimer、TimePicker、Toggle、WaterFlow、XComponent
22
23**参数:**
24
25| 参数名 | 类型                                                 | 必填 | 说明                                                         |
26| ------ | ---------------------------------------------------- | ---- | ------------------------------------------------------------ |
27| modifier  | &nbsp;DrawModifier&nbsp;\|&nbsp;undefined | 是   | 自定义绘制修改器,其中定义了自定义绘制的逻辑。 <br> 默认值:undefined <br/>**说明:** <br/> 每个自定义修改器只对当前绑定组件的FrameNode生效,对其子节点不生效。 |
28
29## DrawModifier
30
31DrawModifier可设置前景(drawFront)、内容(drawContent)和背景(drawBehind)的绘制方法,还提供主动触发重绘的方法invalidate。每个DrawModifier实例只能设置到一个组件上,禁止进行重复设置。
32
33**系统能力:** SystemCapability.ArkUI.ArkUI.Full
34
35### drawFront
36
37drawFront?(drawContext: DrawContext): void
38
39自定义绘制前景的接口,若重载该方法则可进行前景的自定义绘制。
40
41**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
42
43**系统能力:** SystemCapability.ArkUI.ArkUI.Full
44
45**参数:**
46
47| 参数名  | 类型                                                   | 必填 | 说明             |
48| ------- | ------------------------------------------------------ | ---- | ---------------- |
49| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | 是   | 图形绘制上下文。 |
50
51### drawContent
52
53drawContent?(drawContext: DrawContext): void
54
55自定义绘制内容的接口,若重载该方法可进行内容的自定义绘制,会替换组件原本的内容绘制函数。
56
57**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
58
59**系统能力:** SystemCapability.ArkUI.ArkUI.Full
60
61**参数:**
62
63| 参数名  | 类型                                                   | 必填 | 说明             |
64| ------- | ------------------------------------------------------ | ---- | ---------------- |
65| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | 是   | 图形绘制上下文。 |
66
67### drawBehind
68
69drawBehind?(drawContext: DrawContext): void
70
71自定义绘制背景的接口,若重载该方法则可进行背景的自定义绘制。
72
73**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
74
75**系统能力:** SystemCapability.ArkUI.ArkUI.Full
76
77**参数:**
78
79| 参数名  | 类型                                                   | 必填 | 说明             |
80| ------- | ------------------------------------------------------ | ---- | ---------------- |
81| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | 是   | 图形绘制上下文。 |
82
83
84### invalidate
85
86invalidate(): void
87
88主动触发重绘的接口,开发者无需也无法重载,调用会触发所绑定组件的重绘。
89
90**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
91
92**系统能力:** SystemCapability.ArkUI.ArkUI.Full
93
94## 示例
95
96通过DrawModifier对Text组件进行自定义绘制。
97
98```ts
99// xxx.ets
100import { drawing } from '@kit.ArkGraphics2D';
101import { AnimatorResult } from '@kit.ArkUI';
102
103class MyFullDrawModifier extends DrawModifier {
104  public scaleX: number = 1;
105  public scaleY: number = 1;
106
107  drawBehind(context: DrawContext): void
108  {
109    const brush = new drawing.Brush();
110    brush.setColor({
111      alpha: 255,
112      red: 255,
113      green: 0,
114      blue: 0
115    });
116    context.canvas.attachBrush(brush);
117    const halfWidth = context.size.width / 2;
118    const halfHeight = context.size.width / 2;
119    context.canvas.drawRect({
120      left: vp2px(halfWidth - 50 * this.scaleX),
121      top: vp2px(halfHeight - 50 * this.scaleY),
122      right: vp2px(halfWidth + 50 * this.scaleX),
123      bottom: vp2px(halfHeight + 50 * this.scaleY)
124    });
125  }
126
127  drawContent(context: DrawContext): void
128  {
129    const brush = new drawing.Brush();
130    brush.setColor({
131      alpha: 255,
132      red: 0,
133      green: 255,
134      blue: 0
135    });
136    context.canvas.attachBrush(brush);
137    const halfWidth = context.size.width / 2;
138    const halfHeight = context.size.width / 2;
139    context.canvas.drawRect({
140      left: vp2px(halfWidth - 30 * this.scaleX),
141      top: vp2px(halfHeight - 30 * this.scaleY),
142      right: vp2px(halfWidth + 30 * this.scaleX),
143      bottom: vp2px(halfHeight + 30 * this.scaleY)
144    });
145  }
146
147  drawFront(context: DrawContext): void
148  {
149    const brush = new drawing.Brush();
150    brush.setColor({
151      alpha: 255,
152      red: 0,
153      green: 0,
154      blue: 255
155    });
156    context.canvas.attachBrush(brush);
157    const halfWidth = context.size.width / 2;
158    const halfHeight = context.size.width / 2;
159    const radiusScale = (this.scaleX + this.scaleY) / 2;
160    context.canvas.drawCircle(vp2px(halfWidth), vp2px(halfHeight), vp2px(20 * radiusScale));
161  }
162}
163
164class MyFrontDrawModifier extends DrawModifier {
165  public scaleX: number = 1;
166  public scaleY: number = 1;
167
168  drawFront(context: DrawContext): void
169  {
170    const brush = new drawing.Brush();
171    brush.setColor({
172      alpha: 255,
173      red: 0,
174      green: 0,
175      blue: 255
176    });
177    context.canvas.attachBrush(brush);
178    const halfWidth = context.size.width / 2;
179    const halfHeight = context.size.width / 2;
180    const radiusScale = (this.scaleX + this.scaleY) / 2;
181    context.canvas.drawCircle(vp2px(halfWidth), vp2px(halfHeight), vp2px(20 * radiusScale));
182  }
183}
184
185@Entry
186@Component
187struct DrawModifierExample {
188  private fullModifier: MyFullDrawModifier = new MyFullDrawModifier();
189  private frontModifier: MyFrontDrawModifier = new MyFrontDrawModifier();
190  private drawAnimator: AnimatorResult | undefined = undefined;
191  @State modifier: DrawModifier = new MyFrontDrawModifier();
192  private count = 0;
193
194  create() {
195    let self = this;
196    this.drawAnimator = this.getUIContext().createAnimator({
197      duration: 1000,
198      easing: 'ease',
199      delay: 0,
200      fill: 'forwards',
201      direction: 'normal',
202      iterations: 1,
203      begin: 0,
204      end: 2
205    });
206    this.drawAnimator.onFrame = (value: number) => {
207      console.log('frame value =', value);
208      const tempModifier = self.modifier as MyFullDrawModifier | MyFrontDrawModifier;
209      tempModifier.scaleX = Math.abs(value - 1);
210      tempModifier.scaleY = Math.abs(value - 1);
211      self.modifier.invalidate();
212    };
213  }
214
215  build() {
216    Column() {
217      Row() {
218        Text('test text')
219        .width(100)
220        .height(100)
221        .margin(10)
222        .backgroundColor(Color.Gray)
223        .onClick(() => {
224          const tempModifier = this.modifier as MyFullDrawModifier | MyFrontDrawModifier;
225          tempModifier.scaleX -= 0.1;
226          tempModifier.scaleY -= 0.1;
227        })
228        .drawModifier(this.modifier)
229      }
230      Row() {
231        Button('create')
232        .width(100)
233        .height(100)
234        .margin(10)
235        .onClick(() => {
236          this.create();
237        })
238        Button('play')
239        .width(100)
240        .height(100)
241        .margin(10)
242        .onClick(() => {
243          if (this.drawAnimator) {
244            this.drawAnimator.play();
245          }
246        })
247        Button('changeModifier')
248        .width(100)
249        .height(100)
250        .margin(10)
251        .onClick(() => {
252          this.count += 1;
253          if (this.count % 2 === 1) {
254            console.log('change to full modifier');
255            this.modifier = this.fullModifier;
256          } else {
257            console.log('change to front modifier');
258            this.modifier = this.frontModifier;
259          }
260        })
261      }
262    }
263    .width('100%')
264    .height('100%')
265  }
266}
267```
268
269![drawModifier.gif](figures/drawModifier.gif)
270