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