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 | [DrawModifier](#drawmodifier-1) \| undefined | 是 | 自定义绘制修改器,其中定义了自定义绘制的逻辑。 <br> 默认值:undefined <br/>**说明:** <br/> 每个自定义修改器只对当前绑定组件的FrameNode生效,对其子节点不生效。 | 28 29## DrawModifier 30 31DrawModifier可设置前景(drawFront)、内容(drawContent)和背景(drawBehind)的绘制方法,还提供主动触发重绘的方法invalidate。每个DrawModifier实例只能设置到一个组件上,禁止进行重复设置。 32 33**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 34 35**系统能力:** SystemCapability.ArkUI.ArkUI.Full 36 37### drawFront 38 39drawFront?(drawContext: DrawContext): void 40 41自定义绘制前景的接口,若重载该方法则可进行前景的自定义绘制。 42 43**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 44 45**系统能力:** SystemCapability.ArkUI.ArkUI.Full 46 47**参数:** 48 49| 参数名 | 类型 | 必填 | 说明 | 50| ------- | ------------------------------------------------------ | ---- | ---------------- | 51| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | 是 | 图形绘制上下文。 | 52 53### drawContent 54 55drawContent?(drawContext: DrawContext): void 56 57自定义绘制内容的接口,若重载该方法可进行内容的自定义绘制,会替换组件原本的内容绘制函数。 58 59**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 60 61**系统能力:** SystemCapability.ArkUI.ArkUI.Full 62 63**参数:** 64 65| 参数名 | 类型 | 必填 | 说明 | 66| ------- | ------------------------------------------------------ | ---- | ---------------- | 67| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | 是 | 图形绘制上下文。 | 68 69### drawBehind 70 71drawBehind?(drawContext: DrawContext): void 72 73自定义绘制背景的接口,若重载该方法则可进行背景的自定义绘制。 74 75**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 76 77**系统能力:** SystemCapability.ArkUI.ArkUI.Full 78 79**参数:** 80 81| 参数名 | 类型 | 必填 | 说明 | 82| ------- | ------------------------------------------------------ | ---- | ---------------- | 83| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | 是 | 图形绘制上下文。 | 84 85 86### invalidate 87 88invalidate(): void 89 90主动触发重绘的接口,开发者无需也无法重载,调用会触发所绑定组件的重绘。 91 92**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 93 94**系统能力:** SystemCapability.ArkUI.ArkUI.Full 95 96## 示例 97 98通过DrawModifier对Text组件进行自定义绘制。 99 100```ts 101// xxx.ets 102import { drawing } from '@kit.ArkGraphics2D'; 103import { AnimatorResult } from '@kit.ArkUI'; 104 105class MyFullDrawModifier extends DrawModifier { 106 public scaleX: number = 1; 107 public scaleY: number = 1; 108 109 drawBehind(context: DrawContext): void { 110 const brush = new drawing.Brush(); 111 brush.setColor({ 112 alpha: 255, 113 red: 255, 114 green: 0, 115 blue: 0 116 }); 117 context.canvas.attachBrush(brush); 118 const halfWidth = context.size.width / 2; 119 const halfHeight = context.size.width / 2; 120 context.canvas.drawRect({ 121 left: vp2px(halfWidth - 50 * this.scaleX), 122 top: vp2px(halfHeight - 50 * this.scaleY), 123 right: vp2px(halfWidth + 50 * this.scaleX), 124 bottom: vp2px(halfHeight + 50 * this.scaleY) 125 }); 126 } 127 128 drawContent(context: DrawContext): void { 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 const brush = new drawing.Brush(); 149 brush.setColor({ 150 alpha: 255, 151 red: 0, 152 green: 0, 153 blue: 255 154 }); 155 context.canvas.attachBrush(brush); 156 const halfWidth = context.size.width / 2; 157 const halfHeight = context.size.width / 2; 158 const radiusScale = (this.scaleX + this.scaleY) / 2; 159 context.canvas.drawCircle(vp2px(halfWidth), vp2px(halfHeight), vp2px(20 * radiusScale)); 160 } 161} 162 163class MyFrontDrawModifier extends DrawModifier { 164 public scaleX: number = 1; 165 public scaleY: number = 1; 166 167 drawFront(context: DrawContext): void { 168 const brush = new drawing.Brush(); 169 brush.setColor({ 170 alpha: 255, 171 red: 0, 172 green: 0, 173 blue: 255 174 }); 175 context.canvas.attachBrush(brush); 176 const halfWidth = context.size.width / 2; 177 const halfHeight = context.size.width / 2; 178 const radiusScale = (this.scaleX + this.scaleY) / 2; 179 context.canvas.drawCircle(vp2px(halfWidth), vp2px(halfHeight), vp2px(20 * radiusScale)); 180 } 181} 182 183@Entry 184@Component 185struct DrawModifierExample { 186 private fullModifier: MyFullDrawModifier = new MyFullDrawModifier(); 187 private frontModifier: MyFrontDrawModifier = new MyFrontDrawModifier(); 188 private drawAnimator: AnimatorResult | undefined = undefined; 189 @State modifier: DrawModifier = new MyFrontDrawModifier(); 190 private count = 0; 191 192 create() { 193 let self = this; 194 this.drawAnimator = this.getUIContext().createAnimator({ 195 duration: 1000, 196 easing: 'ease', 197 delay: 0, 198 fill: 'forwards', 199 direction: 'normal', 200 iterations: 1, 201 begin: 0, 202 end: 2 203 }); 204 this.drawAnimator.onFrame = (value: number) => { 205 console.log('frame value =', value); 206 const tempModifier = self.modifier as MyFullDrawModifier | MyFrontDrawModifier; 207 tempModifier.scaleX = Math.abs(value - 1); 208 tempModifier.scaleY = Math.abs(value - 1); 209 self.modifier.invalidate(); 210 }; 211 } 212 213 build() { 214 Column() { 215 Row() { 216 Text('test text') 217 .width(100) 218 .height(100) 219 .margin(10) 220 .backgroundColor(Color.Gray) 221 .onClick(() => { 222 const tempModifier = this.modifier as MyFullDrawModifier | MyFrontDrawModifier; 223 tempModifier.scaleX -= 0.1; 224 tempModifier.scaleY -= 0.1; 225 }) 226 .drawModifier(this.modifier) 227 } 228 229 Row() { 230 Button('create') 231 .width(100) 232 .height(100) 233 .margin(10) 234 .onClick(() => { 235 this.create(); 236 }) 237 Button('play') 238 .width(100) 239 .height(100) 240 .margin(10) 241 .onClick(() => { 242 if (this.drawAnimator) { 243 this.drawAnimator.play(); 244 } 245 }) 246 Button('changeModifier') 247 .width(100) 248 .height(100) 249 .margin(10) 250 .onClick(() => { 251 this.count += 1; 252 if (this.count % 2 === 1) { 253 console.log('change to full modifier'); 254 this.modifier = this.fullModifier; 255 } else { 256 console.log('change to front modifier'); 257 this.modifier = this.frontModifier; 258 } 259 }) 260 } 261 } 262 .width('100%') 263 .height('100%') 264 } 265} 266``` 267 268 269