Android自定义View
简单复习一下Android中自定义View的流程:
创建一个类CustomView继承自View然后实现如下方法:
onDraw方法: 进行绘制操作, 确定自身的内容onMeasure方法: 进行测量操作, 确定自身的大小, 位置由parent决定onTouchEvent方法: 进行手势处理
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {override fun onTouchEvent(event: MotionEvent?): Boolean {//进行手势处理return super.onTouchEvent(event)}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {//进行测量操作, 确定自身的大小, 位置由parent决定}override fun onDraw(canvas: Canvas) {//进行绘制操作, 确定自身的内容}}
自定义属性通过xml配置:
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="CustomView"><attr name="custom_attr" format="boolean" /></declare-styleable>
</resources>
这样一个自定义View就实现了…
Flutter自定义Widget
那么在Flutter中, 怎么自定义Widget呢? 准确来说, 应该是自定义RenderObject.
这里请忽略
CustomPaint小部件…
class CustomRenderObject extends RenderBox {bool hitTestSelf(Offset position) {return true;}void handleEvent(PointerEvent event, BoxHitTestEntry entry) {//进行手势处理, 这里需要注意的是,//如果想要`handleEvent`方法被回调, 那么`hitTestSelf`必须返回true}void performLayout() {//进行测量操作, 确定自身的大小, 位置由parent决定}void paint(PaintingContext context, Offset offset) {//进行绘制操作, 确定自身的内容}
}
class CustomWidget extends LeafRenderObjectWidget {const CustomWidget({super.key});RenderObject createRenderObject(BuildContext context) {return CustomRenderObject();}
}
这里的CustomWidget相对于Android中自定义View的配置属性.
注意
如果想要handleEvent回调, 那么hitTestSelf方法必须返回true, 否则不会触发.
总结
| Android | Flutter | |
|---|---|---|
| 绘制入口 | View.onDraw | RenderObject.paint |
| 测量入口 | View.onMeasure | RenderObject.performLayout |
| 手势入口 | View.onTouchEvent | RenderObject.handleEvent |
| 属性配置 | xml | Widget |
绘制相关 | Canvas | PaintingContext.canvas |
绘制图像 | Canvas.drawBitmap | Canvas.drawImage |
绘制图形 | Canvas.drawPath | Canvas.drawPath |
绘制文本 | Canvas.drawText | TextPainter.paint |
触发重新绘制 | View.invalidate | RenderObject.markNeedsPaint |
触发重新布局 | View.requestLayout | RenderObject.markNeedsLayout |
完整示例代码:
class CustomWidget extends LeafRenderObjectWidget {const CustomWidget({super.key});RenderObject createRenderObject(BuildContext context) {return CustomRenderObject();}
}/// [RenderSliver]
class CustomRenderObject extends RenderBox {Offset localPosition = Offset.zero;bool hitTestSelf(Offset position) {return true;}void handleEvent(PointerEvent event, BoxHitTestEntry entry) {//进行手势处理, 这里需要注意的是,//如果想要`handleEvent`方法被回调, 那么`hitTestSelf`必须返回truelocalPosition = event.localPosition;markNeedsPaint();//markNeedsLayout();}void performLayout() {//进行测量操作, 确定自身的大小, 位置由parent决定size = constraints.constrain(const Size(100, 100));}void paint(PaintingContext context, Offset offset) {//进行绘制操作, 确定自身的内容//context.canvas.drawImage(image, offset, paint);//context.canvas.drawPath(path, paint);//TextPainter()..layout()..paint(canvas, offset);context.canvas.drawRect(paintBounds.shift(offset),Paint()..style = PaintingStyle.stroke..color = Colors.purpleAccent,);context.canvas.drawCircle(localPosition + offset,10,Paint()..style = PaintingStyle.fill..color = Colors.purpleAccent,);TextPainter(text: TextSpan(text: localPosition.toString(),style: const TextStyle(color: Colors.purpleAccent,fontSize: 8,),),textDirection: TextDirection.ltr)..layout()..paint(context.canvas, offset);}
}
这里介绍一下LeafRenderObjectWidget SingleChildRenderObjectWidget和MultiChildRenderObjectWidget的区别:
| 类名 | 说明 |
|---|---|
| LeafRenderObjectWidget | 不接受任何子Widget |
| SingleChildRenderObjectWidget | 接受一个子Widget |
| MultiChildRenderObjectWidget | 接受一组子Widget |
附加
RenderObject有2个关键的子类RenderBox和RenderSliver
RenderBox对应的是BoxConstraints约束, 也叫盒子约束;
RenderSliver对应的是SliverConstraints约束, 也叫条子约束;
BoxConstraints
const BoxConstraints({this.minWidth = 0.0,this.maxWidth = double.infinity,this.minHeight = 0.0,this.maxHeight = double.infinity,
});
盒子约束, 就是简单的约束大小使用. 这个在Flutter非常常见, 也是用得最多的一种约束.只要宽高即可.
SliverConstraints
const SliverConstraints({required this.axisDirection,required this.growthDirection,required this.userScrollDirection,required this.scrollOffset,required this.precedingScrollExtent,required this.overlap,required this.remainingPaintExtent,required this.crossAxisExtent,required this.crossAxisDirection,required this.viewportMainAxisExtent,required this.remainingCacheExtent,required this.cacheOrigin,
});
条子约束就比较复杂, 通常在ListView GridView可滚动的小部件中用得最多, 这是Flutter中用来协调滚动事件非常重要的约束, 这你就不展开了…
源码地址
还有一篇类比Android自定义ViewGroup的文章即将发布…
群内有各(pian)种(ni)各(jin)样(qun)的大佬,等你来撩.
联系作者
点此QQ对话 该死的空格 点此快速加群


