一、如何使用Canvas draw/paint
在Android中,您可以使用Canvas在屏幕上繪製自定義形狀。但是在Flutter我們要藉助CustomPaint和CustomPainter類來幫助我們繪製畫布,它們實現您的算法以繪製各種圖案到畫布。
二、CustomPaint和CustomPainter
1、CustomPaint 介紹
CustomPaint讓用戶能夠自定義widget,它提供了canvas用戶可以通過這個canvas來繪製widget,CustomPaint會先調用painter繪製背景,然後再繪製child,最後調用foregroundPainter來繪製前景,CustomPaint的定義如下
const CustomPaint({
Key key,
this.painter,
this.foregroundPainter,
this.size = Size.zero,
this.isComplex = false,
this.willChange = false,
Widget child,
})
painter
:負責繪製背景的painterforegroundPainter
: 負責繪製前景的paintersize
: 控件大小isComplex
: 是否複雜繪製,需要用cache來提高繪製效率willChange
: 和isComplex配合使用,當啓用緩存時,該屬性代表在下一幀中繪製是否會改變child
: 子widget
2、CustomPainter 介紹
CustomPaint的繪製過程都將會交給CustomPainter來完成,CustomPainter是個抽象類,初始化CustomPainter的時候必須要重寫它的paint
跟 shouldRepaint
接口,可以根據自己的場景來選擇重寫hitTest
跟shouldRebuildSemantics
方法。
paint
: 每當CustomPaint需要重繪的時候都會調用此接口shouldRepaint
: 當CustomPaint被重新設置了一個新的painter後會回調此方法,CustomPaint會根據shouldRepaint的返回值來判斷是否需要重新繪製ui,譬如新的painter跟舊的painter繪製的內容不一樣時,此時shouldRepaint需要返回true來通知CustomPaint重新繪製。
3、Canvas 介紹
提供圖形操作界面,canvas提供了各種繪製接口來繪製圖形。真正的繪製是由canvas跟paint來完成的
//畫圓
drawCircle(Offset c, double radius, Paint paint) → void
//畫圖片
drawImage(Image image, Offset p, Paint paint) → void
//畫九宮圖
drawImageNine(Image image, Rect center, Rect dst, Paint paint) → void
//畫線
drawLine(Offset p1, Offset p2, Paint paint) → void
//畫橢圓
drawOval(Rect rect, Paint paint) → void
//畫文字
drawParagraph(Paragraph paragraph, Offset offset) → void
//畫Rect區域
drawRect(Rect rect, Paint paint) → void
//畫陰影
drawShadow(Path path, Color color, double elevation, bool transparentOccluder) → void
4、Paint
在canvas上繪製時要使用的畫筆。
color
: 設置畫筆顏色isAntiAlias
: 設置畫筆是否扛鋸齒shader
: 着色器,填充形狀或者畫線時用到,如果沒設置將會使用colorstrokeWidth
: 設置畫筆畫線寬度style
:繪製模式,畫線或充滿
三、下面是對CustomPainter的使用
class Signature extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new SignatureState();
}
}
class SignatureState extends State<Signature> {
List<Offset> _points = <Offset>[];
Widget build(BuildContext context) {
return new GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);
var dy = localPosition.dy;
var dx = localPosition.dx;
var width = referenceBox.size.width;
var height = referenceBox.size.height;
if(0<= dx && dx <= width && 0<= dy && dy<=height){
_points = new List.from(_points)..add(localPosition);
}
//如果不添加判斷,則會在全局範圍內繪製
// _points = new List.from(_points)..add(localPosition);
});
},
behavior: HitTestBehavior.translucent,
onPanEnd: (DragEndDetails details) => _points.add(null),
child: new CustomPaint(
painter: new SignaturePainter(_points),
),
);
}
}
class SignaturePainter extends CustomPainter {
final List<Offset> points;
SignaturePainter(this.points);
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
var paint = new Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null) {
canvas.drawLine(points[i], points[i + 1], paint);
}
}
}
@override
bool shouldRepaint(SignaturePainter oldDelegate) {
// TODO: implement shouldRepaint
return oldDelegate.points != points;
}
}
實現效果: