自定义控件所必须的一个部分就是绘制了,flutter相关的API跟Android的基本大差不差。会Android的话,相对而言,可能会轻松不少。
下面是效果图:
首先需要一组基础控件,最后放一个CustomPaint()
,它的参数painter
就是需要自定义的部分,child
可以继续添加控件,这里就不放child了。
class TestPage extends StatelessWidget {
final RepaintListener listener = new RepaintListener();
final String letter = "A";
@override
Widget build(BuildContext context) {
return new Material(
color: Colors.blue,
child: new Center(
child: SizedBox(
width: 120.0,
height: 60.0,
child: new InkWell(
child: CustomPaint(painter: new _Painter(listener, letter)),
onTap: () {
listener.changeState();
},
),
),
),
);
}
}
然后,需要自己定义一个类RepaintListener
,去实现Listenable
接口,来管理自定义的部分的绘制,这个接口可以接收到一个VoidCallBack
类型的参数,用于控制重绘,我们只要在需要的时候调用它,就能让Painter
重新进行绘制。
class RepaintListener extends Listenable {
bool _pressed = false;
VoidCallback _listener;
@override
void addListener(VoidCallback listener) {
this._listener = listener;
}
@override
void removeListener(VoidCallback listener) {
this._listener = listener;
}
bool getState() {
return _pressed;
}
void changeState(bool value) {
_pressed = value;
// 调用方法,触发重绘
_listener();
}
}
接下来就是整个过程当中的核心部分了,自定义一个类实现CustomPainter
,实现其中必须实现的两个方法,如果需要添加重绘事件,就需要把刚才自定义的RepaintListener
传递进来,然后将它传递给父类的repaint
参数(super(repaint: listener)
),这样在我们需要让它执行重绘时,就能达到想要的效果了,当然如果想要做动画效果,也可以传一个Animation
进去,这样就能将重绘行为,与动画相绑定了,毕竟Animation
也是实现了Listenable
接口的。
另外,文字(以及图片)的绘制,都需要借助Dart
中的相关方法去实现
(import 'dart:ui' as ui;
),
flutter
本身却没有提供相应的方法。代码中的fontFamily
请自动忽略。
class _Painter extends CustomPainter {
final RepaintListener listener;
String letter;
final Color normal_color = Color(0xFFdddddd);
final Color press_color = Colors.grey;
final Color text_color = Colors.blue;
final Paint painter = new Paint();
ui.ParagraphBuilder _paragraph_builder;
ui.Paragraph _paragraph;
_Painter(this.listener, this.letter) : super(repaint: listener);
@override
void paint(Canvas canvas, Size size) {
//剪切画布
Rect rect = Offset.zero & size;
canvas.clipRect(rect);
if (listener.getState()) {
painter.style = PaintingStyle.fill;
painter.color = press_color;
letter = letter.toLowerCase();
} else {
painter.style = PaintingStyle.fill;
painter.color = normal_color;
letter = letter.toUpperCase();
}
// 绘制背景
RRect key = RRect.fromRectAndRadius(rect, Radius.circular(2.0));
canvas.drawRRect(key, painter);
// 绘制文字
_paragraph_builder = ui.ParagraphBuilder(
ui.ParagraphStyle(
fontFamily: "hwxw",
textAlign: TextAlign.center,
fontSize: 22,
fontWeight: FontWeight.bold,
textDirection: TextDirection.ltr,
maxLines: 1,
),
);
_paragraph_builder.pushStyle(ui.TextStyle(
color: text_color, textBaseline: ui.TextBaseline.alphabetic));
_paragraph_builder.addText(letter);
_paragraph = _paragraph_builder.build();
_paragraph.layout(ui.ParagraphConstraints(width: 10.0));
canvas.drawParagraph(
_paragraph,
Offset((size.width - _paragraph.width) / 2,
(size.height - _paragraph.height) / 2));
}
@override
bool shouldRepaint(_Painter old) => false;
}
最后,flutter中Canvas能画的形状,可以从网上找一个,比如 :
点击跳转至某位大神的Canvas方法总结: