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 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 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