Android
自定义View
简单复习一下Android
中自定义View
的流程:
创建一个类CustomView
继承自View
然后实现如下方法:
onDraw
方法: 进行绘制操作, 确定自身的内容onMeasure
方法: 进行测量操作, 确定自身的大小, 位置由parent决定onTouchEvent
方法: 进行手势处理
kotlin
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
<?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
小部件...
dart
class CustomRenderObject extends RenderBox {
@override
bool hitTestSelf(Offset position) {
return true;
}
@override
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
//进行手势处理, 这里需要注意的是,
//如果想要`handleEvent`方法被回调, 那么`hitTestSelf`必须返回true
}
@override
void performLayout() {
//进行测量操作, 确定自身的大小, 位置由parent决定
}
@override
void paint(PaintingContext context, Offset offset) {
//进行绘制操作, 确定自身的内容
}
}
dart
class CustomWidget extends LeafRenderObjectWidget {
const CustomWidget({super.key});
@override
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 |
完整示例代码:
dart
class CustomWidget extends LeafRenderObjectWidget {
const CustomWidget({super.key});
@override
RenderObject createRenderObject(BuildContext context) {
return CustomRenderObject();
}
}
/// [RenderSliver]
class CustomRenderObject extends RenderBox {
Offset localPosition = Offset.zero;
@override
bool hitTestSelf(Offset position) {
return true;
}
@override
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
//进行手势处理, 这里需要注意的是,
//如果想要`handleEvent`方法被回调, 那么`hitTestSelf`必须返回true
localPosition = event.localPosition;
markNeedsPaint();
//markNeedsLayout();
}
@override
void performLayout() {
//进行测量操作, 确定自身的大小, 位置由parent决定
size = constraints.constrain(const Size(100, 100));
}
@override
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
dart
const BoxConstraints({
this.minWidth = 0.0,
this.maxWidth = double.infinity,
this.minHeight = 0.0,
this.maxHeight = double.infinity,
});
盒子约束
, 就是简单的约束大小使用. 这个在Flutter
非常常见, 也是用得最多的一种约束.只要宽高即可.
SliverConstraints
dart
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)
的大佬,等你来撩.