最近再写FlutterDemo,需要用到一个labelview,网上找了找,没有合适的,就对网上一位大神写的demoLabelDemo改了下,就有l了这么一个文字自适应大小的labelViewGithub
说白了就是对CustomPainter 和对画布的操作(旋转和平移)
@override
Widget build(BuildContext context) {
return LabelViewDecoration(
size: Size(80.0, 80.0),//label size
labelColor: Colors.red,//label color
labelAlignment: LabelAlignment.leftTop,//The orientation of the label attached to the widget
useAngle: true,//
labelText: "Hot",
labelTextColor: Colors.white,
child: _builderItem()//Label attached widget
);
}
其中最重要的就是对形状和文字的绘制,形状的绘制采用Path,就是以size尺寸绘制一个三角形,根据三角形的大小测量文字的大小,然后旋转画布的角度,然后平移。画布默认旋转中心为坐标轴原点,而且貌似不能更改,至少我没找到,所以需要旋转后再平移,对canvas的位置操作需要倒着写,所以实际代码是先写translate,再写rotate。其余的就是数学计算了。
@override
void paint(Canvas canvas, Size size) {
var drawSize = size.height > size.width ? size.width / 2 : size.height / 2;
Path path = new Path();
switch (labelAlignment) {
case LabelAlignment.leftTop:
if (!useAngle) {
path.moveTo(drawSize / 2, 0);
path.lineTo(0, drawSize / 2);
}
path.lineTo(0, drawSize);
path.lineTo(drawSize, 0);
break;
case LabelAlignment.leftBottom:
path.moveTo(0, size.height - drawSize);
if (useAngle) {
path.lineTo(drawSize, size.height);
path.lineTo(0, size.height);
} else {
path.lineTo(0, size.height - drawSize / 2);
path.lineTo(drawSize / 2, size.height);
path.lineTo(drawSize, size.height);
}
break;
case LabelAlignment.rightTop:
path.moveTo(size.width - drawSize, 0);
if (useAngle) {
path.lineTo(size.width, 0);
} else {
path.lineTo(size.width - drawSize / 2, 0);
path.lineTo(size.width, drawSize / 2);
}
path.lineTo(size.width, drawSize);
break;
case LabelAlignment.rightBottom:
if (useAngle) {
path.moveTo(size.width, size.height);
path.lineTo(size.width - drawSize, size.height);
path.lineTo(size.width, size.height - drawSize);
} else {
path.moveTo(size.width - drawSize, size.height);
path.lineTo(size.width - drawSize / 2, size.height);
path.lineTo(size.width, size.height - drawSize / 2);
path.lineTo(size.width, size.height - drawSize);
}
break;
default:
if (!useAngle) {
path.moveTo(drawSize / 2, 0);
path.lineTo(0, drawSize / 2);
}
path.lineTo(0, drawSize);
path.lineTo(drawSize, 0);
break;
}
path.close();
canvas.drawPath(path, _paint);
canvas.save();
//计算字体size
double minWidth = 0;
switch(labelAlignment){
case LabelAlignment.leftTop:
//旋转画布并平移 代码上是先做平移在旋转操作,反着来
canvas.translate(0.0, drawSize);
canvas.rotate(angleToRadian(-45.0));
if (useAngle) {
double x = ((drawSize / 2) * (drawSize / 2) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize);
canvas.drawParagraph(paragraph, Offset(0, -drawSize / 2));
} else {
double x = ((drawSize / 3) * (drawSize / 3) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize / 2);
canvas.drawParagraph(paragraph, Offset(drawSize / 3, -drawSize / 3));
}
break;
case LabelAlignment.leftBottom:
canvas.translate(0.0, drawSize);
canvas.rotate(angleToRadian(45.0));
if (useAngle) {
double x = ((drawSize / 2) * (drawSize / 2) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize);
canvas.drawParagraph(paragraph, Offset(0, 0));
} else {
double x = ((drawSize / 3) * (drawSize / 3) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize / 2);
canvas.drawParagraph(paragraph, Offset(drawSize / 3, drawSize /27));
}
break;
case LabelAlignment.rightTop:
//旋转画布并平移 代码上是先做平移在旋转操作,反着来
canvas.translate(0.0, drawSize);
canvas.rotate(angleToRadian(45.0));
if (useAngle) {
double x = ((drawSize / 2) * (drawSize / 2) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize);
canvas.drawParagraph(paragraph, Offset(drawSize/8,-drawSize*2));
} else {
double x = ((drawSize / 3) * (drawSize / 3) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize / 2);
canvas.drawParagraph(paragraph, Offset(drawSize/2 , -drawSize*7/4 ));
}
break;
case LabelAlignment.rightBottom:
//旋转画布并平移 代码上是先做平移在旋转操作,反着来
canvas.translate(0.0, drawSize);
canvas.rotate(angleToRadian(-45.0));
if (useAngle) {
double x = ((drawSize / 2) * (drawSize / 2) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize);
canvas.drawParagraph(paragraph, Offset(drawSize*8/5, -drawSize/4));
} else {
double x = ((drawSize / 3) * (drawSize / 3) / 2);
minWidth = sqrt(x);
//方法一:使用drawParagraph绘制文字,Paragraph是ui包内的
Paragraph paragraph = buildParagraph(labelText, minWidth, drawSize / 2);
canvas.drawParagraph(paragraph, Offset(drawSize*2, -drawSize/5));
}
break;
default:
break;
}
canvas.restore();
}
//根据文本内容和字体大小等构建一段文本
Paragraph buildParagraph(String text, double textSize, double constWidth) {
ParagraphBuilder builder = ParagraphBuilder(
ParagraphStyle(
textAlign: TextAlign.right,
fontSize: textSize,
fontWeight: FontWeight.normal,
),
);
builder.pushStyle(ui.TextStyle(color: labelTextColor));
builder.addText(text);
ParagraphConstraints constraints = ParagraphConstraints(width: constWidth);
return builder.build()..layout(constraints);
}