在前面的几篇文章中我们介绍了Flutter中自定义view的用法,学习了canvas中常用的绘制方法,在这篇及以后的几篇文章中我会给大家写几个自定义View的例子。
提起标签相信大家都不会陌生,在平时使用应用或者网页中会经常看到这种效果
比如这种
或者这种?
今天我们就使用前面学过的知识来完成这个效果如何?
首先我们还是先建立类继承于CustomPainter
这个时候我们先不着急去绘制图形,我们先来你想下canvas的哪个方法绘制比较方便。
由于这个LabelView是一个不算很规则的图形,所以我们用 canvas.drawPath()方法来实现比较合适。
接下来我们先来完成一个简单的绘制,先实现左上角的三角形效果
在这里我们先假设这个label的边长为100
可以看到我们使用Path构建了一个三角形的区域,并设置画笔的绘制风格为fill
当然,我们也可以改变这个label显示位置
比如右上角:
比如左下角:
比如右下角:
当然这个时候我们绘制的label的颜色和大小都是我们写死在自定义的View里面的,包括标签显示的位置也都是我们默认写在左上角的。
接下来我们尝试把这些属性由外部传入进来。
首先我们新建类LabelAlignment来标注Label的方向
常量名 | 作用 |
---|---|
leftTop | 显示在左上角 |
leftBottom | 显示在左下角 |
rightTop | 显示在右上角 |
rightBottom | 显示在右下角 |
然后我们让我们自定义的LabelViewPainter的构造方法传入这个参数。
同样的我们让LabelViewPainter的构造方法中传入label的颜色,方便我们绘制
@override void paint(Canvas canvas, Size size) { }
我们使用paint传入的size属性来获取label的尺寸。
在这里我们取size的宽和高最小值的二分之一为我们label的边长。
var drawSize = size.height > size.width ? size.width / 2 : size.height / 2;
然后我们根据传入的LabelAlignment类型来绘制不同方向的label即可。
下面看下改过的代码:
可以看到,我们只需要在调用的地方传入Size、color以及label方向即可调用LabelView,为了方便看出效果,我们给外层加了一个灰色的背景。
当然,我们依然可以把Size、color以及label设置为可选参数
LabelView({this.size=Size.infinite, this.labelColor = Colors.blue, this.labelAlignment=LabelAlignment.leftTop});
设置LabelView的默认尺寸为充满屏幕、默认颜色为蓝色、默认位置在左上角。这样一来我们使用起来就会简单很多了
在上面的文章中我们实现了带角的LabelView的效果,但是在平时的使用中我们也会常常使用哪种没有角的Label效果。
下面我们就来看下如何实现:
其实在上面代码的基础上实现起来就显得非常的简单,我们只需要根据显示的位置控制绘制的path即可。
为了方便使用,同样的我们给它新增了一个属性叫做“useAngle”默认我们让它是true,当它为false时显示去角的label效果。
看下实现的代码:
说了这么多,我们上面也仅仅是画出来了一个Label图形,文字没有显示啊,怎么让它显示文字呢?
但是,根据我们前面学过的东西我们知道,在CustomPainter中canvas是没有办法绘制文字的,那怎么办?总不能不显示文字吧?
方法当然是有的,大家都知道CustomPainter的使用时需要借助于CustomPaint,而CustomPaint可以传入child哦。
所以我们就可以从这里出发,把我们想要的文字放在这个child里。
但是我们并不能简简单单的就直接把我们要绘制的文字放在child里,因为我们的Label是倾斜且在一定的位置显示的。
所以我们需要控制文字的切斜和位置在我们绘制的label上才行。
在这里我们需要借助 Transform.rotate()来实现这个效果
Transform.rotate
构造方法非常的简单,我们只需要计算出旋转的角度和起始位置的坐标即可,数学计算就不再具体讲了,由于这里我没有计算文字的宽高所以可能会有一点的误差(原谅我比较懒)
当然,这个计算肯定也是要根据你label显示的位置来算的。
所以,我们需要在爱我们LabelView的构造方法中再增加几个关于文字的属性,文字内容、文字风格
改动比较多,所以把LabelView部分的代码都放了出来
在调用的地方我们只需要更改labelView的颜色、位置、文字即可。
你以为实现到上面的效果就结束了吗?不是这个样子的,因为我们LabView作为一个Widget只能显示不能组合其他Widget就不符合Flutter Widget设计的理念(组合大于继承)啊,所以我们的LabelView肯定也要支持组合其他的Widget的。
但是,看上面的代码可以发现我们CustomPaint的child已经被文字给占用了,但是我们现在还是需要在这个child里去放子Widget,怎么办?
肯定是要在这个child放置一个支持多child的Widget啊,想想也就那个几个
Widget | 说明 |
---|---|
ROW | 横向显示 |
Colum | 纵向显示 |
ListView | 列表显示 |
Expanded | 子Widget按照比例分布 |
Table | 表格布局显示 |
Stack | 堆栈布局 |
IndexedStack | 可控制堆栈布局 |
根据我们以前学过的知识不难发现,我们这里使用Stack时最好的,位于栈定的Widget会覆盖位于栈底的Widget。
所以,我们把用于组合的子child放在栈底(第一个位置,优先入栈),把我们自定义的LabelView放在第二个位置,这样我们就实现了LabelView覆盖在子child上的效果。
在调用的地方:
效果如下:
当然,这个labelView实现的还是比较粗糙的,感兴趣的小伙伴可以继续去完善,如如labelView半透明效果,文字的测量等等。