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对话 该死的空格
点此快速加群