前言部分
这段时间陆续学习flutter框架,因为一直坚信跨平台开发会是下一次的变革,以前也接触过一些跨平台的框架,但是觉得都不太好用的,如RN,cordova等。RN我是写过一些demo的,但是发现调试和代码书写都很麻烦,后期就没有深入研究,Cordova的话是在项目中了解过,感觉入手有点难,因为Android开发对js了解过于少。因为我把一个Cordova的项目重写成原生项目,期间先是通过jsbridge做了过度。
这次flutter的出现让我觉得,跨平台又有了一种新的可能,所以特意学习,并写了一个小的项目练手。
项目还没完善,有时间我会继续来做的。
内容部分
今天主题是介绍一下flutter中的自定义view,其实和Android很相似,所以我们写起来应该是很简单的,只不过就是flutter中的API还不是很全面,有一些东西还不能完全像做android一样实现,可能后期随着flutter的发展会越来越完善的。
-
首先我们想自定义view的话也需要继承一个类就是CustomPainter这个类就类似于android中的view了。
class MyView extends CustomPainter{ @override void paint(Canvas canvas, Size size) { // TODO: implement paint } @override bool shouldRepaint(CustomPainter oldDelegate) { // TODO: implement shouldRepaint return null; } }
有没有和Android很类似的,其中的paint就类似于android中的onDraw和onMeasure了。
shouldRepaint就是是否需要重新绘制的意思。一般的话直接写oldDelegate != this;就行了。就是原来的Widget和当前的不一致,就重新绘制。
-
其实后面的逻辑就和我们写android一样了,在paint进行绘制操作。
@override void paint(Canvas canvas, Size size) { viewCenterX = size.width / 2; viewCenterY = size.height / 2; if (viewCenterX > viewCenterY) { maxRadius = viewCenterY; } else { maxRadius = viewCenterX; } canvas.save(); drawPolygon(canvas); drawMaskLayer(canvas); drawText(canvas); canvas.restore(); }
-
绘制多边形的代码和以前的一样,主要用到了path来完成,并且path的用法和Android高度一致。代码如下:
void drawPolygon(Canvas canvas) { ///每个角的度数 eachAngle = CIRCLE_ANGLE / sideNum; ///找好所有的顶点,连接起来即可 for (int i = 0; i < layerNum; i++) { mPath.reset(); eachRadius = maxRadius / layerNum * (i + 1); for (int j = 0; j < sideNum + 1; j++) { if (j == 0) { mPath.moveTo(viewCenterX + eachRadius, viewCenterY); } else { double x = viewCenterX + eachRadius * cos(degToRad(eachAngle * j)); double y = viewCenterY + eachRadius * sin(degToRad(eachAngle * j)); mPath.lineTo(x, y); } } mPath.close(); canvas.drawPath(mPath, mPaint); } //这个是把顶点连接起来。 drawLineLinkPoint(canvas, eachAngle, eachRadius); }
-
绘制完成多边形后,再绘制上面的遮罩层,然后在完善一下就完成了。
后面其实都是常规操作了
不过要注意的有的方法flutter是不提供的,需要我们自己来实现,比如计算顶点坐标的时候,我们需要自己通过角度和斜边来计算x和y的坐标了。
num degToRad(num deg) => deg * (pi / 180.0);
num radToDeg(num rad) => rad * (180.0 / pi);
我贴一下完整的代码,就不上传demo了。
import 'dart:math';
/// 自定义控件
import 'package:flutter/material.dart';
class SpiderNetView extends CustomPainter {
///多边形几个边
int sideNum = 6;
///默认几层多边形
int layerNum = 6;
///view 的中心点
double viewCenterX;
double viewCenterY;
///半径,最大的半径
double maxRadius;
Paint mPaint;
Path mPath;
final double CIRCLE_ANGLE = 360;
Paint mLayerPaint;
SpiderNetView(int sideNum) {
this.sideNum = sideNum;
mPaint = new Paint();
mPaint.color = randomRGB();
mPaint.isAntiAlias = true;
mPaint.style = PaintingStyle.stroke;
mPath = new Path();
mLayerPaint = new Paint();
mLayerPaint.color = randomARGB();
mLayerPaint.isAntiAlias = true;
mLayerPaint.style = PaintingStyle.fill;
}
@override
void paint(Canvas canvas, Size size) {
viewCenterX = size.width / 2;
viewCenterY = size.height / 2;
if (viewCenterX > viewCenterY) {
maxRadius = viewCenterY;
} else {
maxRadius = viewCenterX;
}
canvas.save();
drawPolygon(canvas);
drawMaskLayer(canvas);
drawText(canvas);
canvas.restore();
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return oldDelegate != this;
}
double eachRadius;
double eachAngle;
void drawPolygon(Canvas canvas) {
///每个角的度数
eachAngle = CIRCLE_ANGLE / sideNum;
///找好所有的顶点,连接起来即可
for (int i = 0; i < layerNum; i++) {
mPath.reset();
eachRadius = maxRadius / layerNum * (i + 1);
for (int j = 0; j < sideNum + 1; j++) {
if (j == 0) {
mPath.moveTo(viewCenterX + eachRadius, viewCenterY);
} else {
double x = viewCenterX + eachRadius * cos(degToRad(eachAngle * j));
double y = viewCenterY + eachRadius * sin(degToRad(eachAngle * j));
mPath.lineTo(x, y);
}
}
mPath.close();
canvas.drawPath(mPath, mPaint);
}
drawLineLinkPoint(canvas, eachAngle, eachRadius);
}
void drawLineLinkPoint(Canvas canvas, double eachAngle, double eachRadius) {
mPath.reset();
for (int i = 0; i < sideNum; i++) {
mPath.moveTo(viewCenterX, viewCenterY);
double x = viewCenterX + eachRadius * cos(degToRad(eachAngle * i));
double y = viewCenterY + eachRadius * sin(degToRad(eachAngle * i));
mPath.lineTo(x, y);
mPath.close();
canvas.drawPath(mPath, mPaint);
}
}
void drawMaskLayer(Canvas canvas) {
mPath.reset();
for (int i = 0; i < sideNum; i++) {
double mRandomInt = randomInt();
double x =
viewCenterX + maxRadius * cos(degToRad(eachAngle * i)) * mRandomInt;
double y =
viewCenterY + maxRadius * sin(degToRad(eachAngle * i)) * mRandomInt;
if (i == 0) {
mPath.moveTo(x, viewCenterY);
} else {
mPath.lineTo(x, y);
}
}
mPath.close();
canvas.drawPath(mPath, mLayerPaint);
}
void drawText(Canvas canvas) {
for (int i = 0; i < sideNum; i++) {
double x = viewCenterX + maxRadius * cos(degToRad(eachAngle * i));
double y = viewCenterY + maxRadius * sin(degToRad(eachAngle * i));
}
}
num degToRad(num deg) => deg * (pi / 180.0);
num radToDeg(num rad) => rad * (180.0 / pi);
Color randomRGB() {
Random random = new Random();
return Color.fromARGB(
255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
Color randomARGB() {
Random random = new Random();
return Color.fromARGB(random.nextInt(180), random.nextInt(255),
random.nextInt(255), random.nextInt(255));
}
double randomInt() {
Random random = new Random();
return (random.nextInt(10) + 1) / 10;
}
}
如果在android中是有直接可以使用的方法的。下面我贴一个我以前写的同样的一个demo,是使用Java代码来实现的。如下:
结尾部分
flutter中的自定义view相对其他的跨平台来说更接近我们平时写的Java项目,这也是对我们android程序员的一个优势。我始终觉得跨平台是移动端的未来趋势,所以自己也会一直关注这方面的消息。
最后由于demo太大了,就不上传了。