我們將用CustomPaint來繪制我們的時鐘贱除。CustomPaint的重要參數(shù)painter和size。painter是一個繼承了CustomPainter的對象寡键,主要實現(xiàn)了繪畫控件的功能掀泳。size指定了控件的大小,如果CustomPaint的child不為空西轩,size的值就是child控件的大小员舵,指定size的值則無效;如果child為空藕畔,所指定的size的值马僻,就是畫布的大小。
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: ClockPainter(datetime,
numberColor: Colors.black,
handColor: Colors.black,
borderColor: Colors.black,
radius: widget.radius),
size: Size(widget.radius * 2, widget.radius * 2),
);
}
ClockPainter繼承了CustomPainter注服,實現(xiàn)了其中兩個重要方法:paint和shouldRepaint韭邓。paint當自定義控件需要重畫時被調(diào)用。shouldRepaint則決定當條件變化時是否需要重畫溶弟。
class ClockPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
}
@override
bool shouldRepaint(ClockPainter oldDelegate) {
return true;
}
}
首先先時鐘的邊框丐巫,代碼如下赴捞,只需drawCircle就可以實現(xiàn)邊框的繪制浴捆。
//draw border
final borderPaint = Paint()
..color = borderColor
..style = PaintingStyle.stroke
..strokeWidth = borderWidth;
canvas.drawCircle(
Offset(radius, radius), radius - borderWidth / 2, borderPaint);
canvas的起始點是畫布的左上角坐標點為起點弟塞,即x,y都為零我抠。drawCircle畫一個指定了半徑的圓苇本。圓的形狀和樣式由Paint對象指定。style決定了是畫圓盤(PaintingStyle.fill)還是圓環(huán)(PaintingStyle.stroke)菜拓。當style設定為PaintingStyle.stroke時瓣窄,strokeWidth就是指定了圓環(huán)的寬度。
其次纳鼎,可以畫時鐘刻度和數(shù)字了俺夕。
List<Offset> secondsOffset = [];
for (var i = 0; i < 60; i++) {
Offset offset = Offset(
cos(degToRad(6 * i - 90)) * secondDistance + radius,
sin(degToRad(6 * i - 90)) * secondDistance + radius);
secondsOffset.add(offset);
}
//draw second point
final secondPPaint = Paint()
..strokeWidth = 2 * scale
..color = numberColor;
if (secondsOffset.length > 0) {
canvas.drawPoints(PointMode.points, secondsOffset, secondPPaint);
}
時鐘的刻度占整個圓弧的360/60=6度,運用數(shù)學公式每個刻度點的坐標贱鄙,然后用drawPoints畫出每一個刻度點劝贸。
canvas.save();
canvas.translate(radius, radius);
for (var i = 0; i < secondsOffset.length; i++) {
if (i % 5 == 0) {
//draw number
canvas.save();
canvas.translate(0.0, -radius + borderWidth * 4);
textPainter.text = new TextSpan(
text: "${(i ~/ 5) == 0 ? "12" : (i ~/ 5)}",
style: TextStyle(
color: numberColor,
fontFamily: 'Times New Roman',
fontSize: 28.0 * scale,
),
);
//helps make the text painted vertically
canvas.rotate(-angle * i);
textPainter.layout();
textPainter.paint(canvas,
new Offset(-(textPainter.width / 2), -(textPainter.height / 2)));
canvas.restore();
}
canvas.rotate(angle);
}
canvas.restore();
canvas.save()保存當前畫布,以便畫完數(shù)字恢復逗宁。
canvas.translate(radius, radius)把畫布的起始點移到畫布的中心映九。
再次保存畫布后,再把起始點移到正上方位置瞎颗,這里是把起始點數(shù)字12的位置件甥。
TextPainter用來畫文字捌议。
canvas.rotate(-angle * i);以當前畫布起始點旋轉(zhuǎn)一個角度,這是為了保證每個數(shù)字在下面旋轉(zhuǎn)到對應的位置后保持豎直顯示引有。
canvas.restore()重置畫布瓣颅,即把畫布的起始點定位到控件的中心位置。
canvas.rotate(angle)以控件中心為原點旋轉(zhuǎn)一個角度譬正,即把數(shù)字旋轉(zhuǎn)到對應的位置宫补。數(shù)字12旋轉(zhuǎn)角度為零,數(shù)字1旋轉(zhuǎn)角度為30度导帝。
所有的數(shù)字都畫完并旋轉(zhuǎn)到對應的位置后即可恢復起始點到控件的左上角。
最后穿铆,我們接著畫時針您单,分針,秒針荞雏。
final hour = datetime.hour;
final minute = datetime.minute;
final second = datetime.second;
// draw hour hand
Offset hourHand1 = Offset(
radius - cos(degToRad(360 / 12 * hour - 90)) * (radius * 0.2),
radius - sin(degToRad(360 / 12 * hour - 90)) * (radius * 0.2));
Offset hourHand2 = Offset(
radius + cos(degToRad(360 / 12 * hour - 90)) * (radius * 0.5),
radius + sin(degToRad(360 / 12 * hour - 90)) * (radius * 0.5));
final hourPaint = Paint()
..color = handColor
..strokeWidth = 8 * scale;
canvas.drawLine(hourHand1, hourHand2, hourPaint);
// draw minute hand
Offset minuteHand1 = Offset(
radius - cos(degToRad(360 / 60 * minute - 90)) * (radius * 0.3),
radius - sin(degToRad(360 / 60 * minute - 90)) * (radius * 0.3));
Offset minuteHand2 = Offset(
radius +
cos(degToRad(360 / 60 * minute - 90)) * (radius - borderWidth * 3),
radius +
sin(degToRad(360 / 60 * minute - 90)) * (radius - borderWidth * 3));
final minutePaint = Paint()
..color = handColor
..strokeWidth = 3 * scale;
canvas.drawLine(minuteHand1, minuteHand2, minutePaint);
// draw second hand
Offset secondHand1 = Offset(
radius - cos(degToRad(360 / 60 * second - 90)) * (radius * 0.3),
radius - sin(degToRad(360 / 60 * second - 90)) * (radius * 0.3));
Offset secondHand2 = Offset(
radius +
cos(degToRad(360 / 60 * second - 90)) * (radius - borderWidth * 3),
radius +
sin(degToRad(360 / 60 * second - 90)) * (radius - borderWidth * 3));
final secondPaint = Paint()
..color = handColor
..strokeWidth = 1 * scale;
canvas.drawLine(secondHand1, secondHand2, secondPaint);
final centerPaint = Paint()
..strokeWidth = 2 * scale
..style = PaintingStyle.stroke
..color = Colors.yellow;
canvas.drawCircle(Offset(radius, radius), 4 * scale, centerPaint);
一個小時占時鐘角度為30度虐秦,利用三角函數(shù)算出時針的兩個點,用drawLine畫出帶寬度的時針凤优。
每一分鐘每一秒中所占的角度為6度悦陋,同樣利用三角函數(shù)算出各自對應的兩點,再畫直線就可以得到分針筑辨,秒針俺驶。
這樣一個簡單的時鐘控件就完成了。github地址