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)的大佬,等你来撩.
联系作者

