Flutter自定义控件分为三大类:
- 组合控件,通过组合其他widget成为一个新的widget。
- 自绘控件,通过使用canvas与paint来完全绘制。
- 继承widget,使用RenderObject来绘制,但最终还是使用canvas来绘制。
本文重点
着重介绍自绘控件,因为所有的widget归根结底都是使用canvas和paint来绘制的,理解了二者,对于其他的widget原理有溯源的功效。
- Canvas:画布
- Paint:画笔 怎么做?
- 继承CustomPainter
- 重写paint方法与shouldRepaint方法
- paint提供来canvas和size,canvas用于绘制,size用于确定大小
- shouldRepaint用于确认是否每次都重新绘制
画什么
canvas.drawRect()画矩形;
void drawRect(Rect rect, Paint paint)
rect 矩形的描述
- Rect.fromCenter({ Offset center, double width, double height }),根据中心点和宽高,定义一个矩形。
- Rect.fromCircle({ Offset center, double radius }),根据中心点和半径定义一个矩形
- Rect.fromLTRB(this.left, this.top, this.right, this.bottom),left左边框距离左边的距离,top上边框距离上边的距离,right右边框距离左边的距离,bottom下边框距离上边的距离。根据这四个值定义一个矩形。
- Rect.fromLTWH(double left, double top, double width, double height),根据左上角顶点和宽高定义一个矩形。
- Rect.fromPoints(Offset a, Offset b),根据左上角顶点和右下角顶点定义一个矩形。
示例:
class _MyHomePageState extends State<MyHomePage> {
bool flag = true;
void change(bool value) {
setState(() {
flag = value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: CustomPaint(
size: Size(380, 560),
painter: MyPainter(),
),
),
);
}
}
class MyPainter extends CustomPainter{
@override
void paint(Canvas canvas, Size size) {
test01(canvas, size);
}
void test00(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
canvas.drawRect(Offset.zero & size, paint);
paint
..color = Colors.green;
canvas.drawRect(Rect.fromCenter(width: 200,height: 200,center: Offset(150, 150)), paint);
paint
..color = Colors.blue;
canvas.drawRect(Rect.fromCircle(radius: 50,center: Offset(150, 150)), paint);
paint
..color = Colors.redAccent;
canvas.drawRect(Rect.fromLTRB(10,10,200,100), paint);
paint
..color = Colors.pink;
canvas.drawRect(Rect.fromLTWH(10,250,100,100), paint);
paint
..color = Colors.grey;
canvas.drawRect(Rect.fromPoints(Offset(250, 250),Offset(350, 300)), paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
canvas.drawRRect()画圆角矩形
void test01(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
Rect rect = Rect.fromCircle(
center: Offset(200, 200), radius: 150);
RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(30));
canvas.drawRRect(rRect, paint);
}
canvas.drawDRRect()画环形圆角矩形
void test011(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
Rect rect1 = Rect.fromCircle(
center: Offset(200, 200), radius: 140);
Rect rect2 = Rect.fromCircle(
center: Offset(200, 200), radius: 160);
RRect rRect1 = RRect.fromRectAndRadius(rect1, Radius.circular(20));
RRect rRect2 = RRect.fromRectAndRadius(rect2, Radius.circular(20));
canvas.drawDRRect(rRect2, rRect1, paint);
}
canvas.drawCircle()画圆
drawCircle(Offset c, double radius, Paint paint)
- c 中心点
- radius 圆半径
- paint 画笔
示例:
void test02(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
canvas.drawRect(Offset.zero & size, paint);
paint ..color = Colors.blue[200];
canvas.drawCircle(Offset(200, 200), 100, paint);
}
canvas.drawOval()画椭圆
void test022(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
canvas.drawRect(Offset.zero & size, paint);
paint..color = Colors.blue[200];
canvas.drawOval(
Rect.fromCenter(width: 200, height: 300, center: Offset(150, 250)),
paint);
}
canvas.drawArc()画弧型
void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
- rect 弧所属椭圆的外接矩形,用于定位该弧的位置
- startAngle,起始角度,按弧度制,顺时针
- sweepAngle,画多少弧度,一个圆的弧度是 2PI,也就是2*3.14
- useCenter,是否将弧与中心连接,形成一个扇形
void test08(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
// ..style = PaintingStyle.fill
..style = PaintingStyle.stroke
..strokeWidth = 5
..isAntiAlias = true;
Rect rect = Rect.fromCircle(
center: Offset(200, 200), radius: 100);
canvas.drawArc(rect, 0, 3.14, false, paint);
}
canvas.drawPoints()画点
参数PointModel:
- ui.PointMode.points,// 单独的点
- ui.PointMode.polygon,// 所有的点按给定顺序连成线
- ui.PointMode.lines,//画一条线,起始点为给定数组的前两个点
void test06(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
canvas.drawRect(Offset.zero & size, paint);
paint
..style = PaintingStyle.stroke
..strokeWidth = 15
..strokeCap = StrokeCap.round
..color = Colors.black;
canvas.drawPoints(
// ui.PointMode.points,// 单独的点
// ui.PointMode.polygon,// 所有的点按给定顺序连成线
ui.PointMode.lines,//画一条线,起始点为给定数组的前两个点
[Offset(100, 100), Offset(300, 100), Offset(200, 300)], paint);
}
canvas.drawRawPoints() 画点
与drawPoints的区别在于,表示点的数据方式不一样。 参数PointModel:
- ui.PointMode.points,// 单独的点
- ui.PointMode.polygon,// 所有的点按给定顺序连成线
- ui.PointMode.lines,//画一条线,起始点为给定数组的前两个点
PointMode.points
PointMode.polygon
PointMode.lines
void _drawRawPoints(Canvas canvas, Size size) {
Float32List points = Float32List.fromList([
-120, -20,-80, -80,-40,
-40,0, -100,40, -140,
80, -160,120, -100]);
Paint paint = new Paint();
paint ..color = Colors.redAccent[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 20;
canvas.drawRawPoints(PointMode.points, points, paint);
// canvas.drawRawPoints(PointMode.lines, points, paint);
// canvas.drawRawPoints(PointMode.polygon, points, paint);
}
canvas.drawVertices() 画带属性的点
void _drawVertices(Canvas canvas, Size size) {
Paint paint = new Paint();
paint
..color = Colors.blue[200]
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = 20;
canvas.drawVertices(
Vertices(VertexMode.triangleFan,
[Offset(50, 50), Offset(50, -50), Offset(-50, -50)],
colors: [Colors.redAccent, Colors.blueAccent, Colors.green],
// indices: [0,0,0],
// textureCoordinates: [Offset(-50, -50), Offset(50, 50), Offset(-50, 50)]
),
BlendMode.srcIn,
paint);
}
canvas.drawLine()画线
void drawLine(Offset p1, Offset p2, Paint paint)
- p1 线的起点
- p2 线的终点
注意:
- 画笔的style 更改为PaintingStyle.stroke
- 可以调整线的粗细 strokeWidth = 3
示例:
void test03(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
canvas.drawRect(Offset.zero & size, paint);
paint
..color = Colors.black
..strokeWidth = 3
..style = PaintingStyle.stroke;
canvas.drawLine(Offset(100, 150), Offset(250, 150), paint);
}
canvas.drawPath()画路径
paint ..color = PaintingStyle.fill;
void test07(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
Path path = Path();
path.moveTo(100, 100);
path.lineTo(100, 200);
path.lineTo(200, 100);
path.lineTo(200, 200);
canvas.drawPath(path, paint);
}
paint ..color = PaintingStyle.stroke;
void test07(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
// ..style = PaintingStyle.fill
..style = PaintingStyle.stroke
..strokeWidth = 5
..isAntiAlias = true;
// 画矩形
Path path = Path();
path.moveTo(100, 100);
path.lineTo(100, 200);
path.lineTo(200, 100);
path.lineTo(200, 200);
canvas.drawPath(path, paint);
}
path.close()
void test07(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
// ..style = PaintingStyle.fill
..style = PaintingStyle.stroke
..strokeWidth = 5
..isAntiAlias = true;
// 画矩形
Path path = Path();
path.moveTo(100, 100);
path.lineTo(100, 200);
path.lineTo(200, 100);
path.lineTo(200, 200);
path.close();
canvas.drawPath(path, paint);
}
canvas.drawColor()画颜色
void drawColor(Color color, BlendMode blendMode)
- blendMode 是颜色的混合模式
void test04(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 画矩形
canvas.drawColor(Colors.blue[200], BlendMode.srcIn);
canvas.drawRect(Offset.zero & size/2, paint);
}
canvas.drawPaint() 用paint画画布
void _drawPaint1(Canvas canvas, Size size) {
Path path = Path();
path.lineTo(80, 80);
path.lineTo(-80, 80);
path.close();
canvas.clipPath(path);
canvas.drawPaint(new Paint()..color = Colors.blueAccent);
}
canvas.drawShadow()画阴影
void drawShadow(Path path, Color color, double elevation, bool transparentOccluder)
- path 阴影区域
- color 阴影颜色
- elevation 阴影高度,一般是向右下
- transparentOccluder 如果阻塞对象不是不透明的,则阻塞“transparentoccluder”参数应为true.百度翻译的,没明白啥意思
void test09(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..strokeWidth = 5
..isAntiAlias = true;
Path path = Path();
path.moveTo(100, 100);
path.lineTo(100, 200);
path.lineTo(200, 100);
path.lineTo(200, 200);
canvas.drawPath(path, paint);
canvas.drawShadow(path, Colors.orange, 10, true);
}
画文字
textpainter.paint()
void _drawTextPaint(Canvas canvas) {
var textpainter = TextPainter(
text: TextSpan(
text: '123456', style: TextStyle(fontSize: 40, color: Colors.red)),
textAlign: TextAlign.center,
textDirection: TextDirection.ltr);
textpainter.layout();
Size size = textpainter.size;
textpainter.paint(canvas, Offset(-size.width / 2, -size.height / 2));
}
canvas.drawParagraph()
void _drawTextWithParagraph(Canvas canvas) {
TextAlign textAlign = TextAlign.center;
var builder = ui.ParagraphBuilder(ui.ParagraphStyle(
textAlign: textAlign,
fontSize: 40,
textDirection: TextDirection.ltr,
maxLines: 1,
));
var builder1 = ui.ParagraphBuilder(ui.ParagraphStyle(
textAlign: TextAlign.right,
fontSize: 40,
textDirection: TextDirection.ltr,
maxLines: 1));
builder.pushStyle(
ui.TextStyle(
color: Colors.black87, textBaseline: ui.TextBaseline.alphabetic),
);
builder1.pushStyle(ui.TextStyle(
color: Colors.red, textBaseline: ui.TextBaseline.alphabetic));
builder.addText("Flutter Can");
builder1.addText("ssssssss");
ui.Paragraph paragraph = builder.build();
ui.Paragraph paragraph1 = builder1.build();
paragraph.layout(ui.ParagraphConstraints(width: 300));
paragraph1.layout(ui.ParagraphConstraints(width: 400));
canvas.drawParagraph(paragraph, Offset(-100, 0));
canvas.drawParagraph(paragraph1, Offset(-200, -110));
canvas.drawRect(Rect.fromLTRB(0, 0, 300, 40),
_paint..color = Colors.blue.withAlpha(33));
}
画图
在根目录建立images 存放图片资源,在pubspec.yaml中声明 assets
注意缩进,assets前是2个空格,- 前是2个空格,- 后是1个空格
图片加载
// 定义从assets加载图片的方法
Future<ui.Image> loadImageFromAssets(String path) async {
ByteData data = await rootBundle.load(path);
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
return decodeImageFromList(bytes);
}
// 在初始时加载图片
@override
void initState() {
super.initState();
_loadImage();
}
void _loadImage() async {
_image = await loadImageFromAssets('images/a2.png');
setState(() {});
}
canvas.drawImage() 画图
void _drawImage(Canvas canvas) {
if (image != null) {
canvas.drawImage(
image, Offset(-image.width / 2, -image.height / 2), _paint);
}
}
canvas.drawImageRect() 画部分图到指定区域
void drawImageRect(Image image, Rect src, Rect dst, Paint paint)
- image,图片源
- src,从图片上选取哪块区域
- dst,画到什么区域
canvas.drawImageRect(
image,
Rect.fromCenter(
center: Offset(image.width / 2, image.height / 2),
width: 180,
height: 180),
Rect.fromLTRB(0, 0, 200, 200),
_paint);
canvas.drawImageNine() 画.9图
void drawImageNine(Image image, Rect center, Rect dst, Paint paint)
- image,源图片
- cennter,设定拉伸区域
- dst,画到什么位置
在填充dst时,如果宽度或高度不够,就以center为中心,划分成三行三列,中间行中间列是拉伸区域
canvas.drawImageNine(
image,
Rect.fromCenter(
center: Offset(image.width / 2, image.height - 150.0),
width: 1,
height: 1.0),
Rect.fromCenter(
center: Offset(
0,
0,
),
width: 430,
height: 430),
_paint);
canvas.drawAtlas()
从一个图上,截取指定区域rects的图片,并放到trasforms的区域内
void drawAtlas(Image atlas, List transforms, List rects, List? colors, BlendMode? blendMode, Rect? cullRect, Paint paint)
void _drawAtlas(Canvas canvas) {
Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2;
Rect cullRect = Rect.fromLTRB(50, 50, 50, 50);
canvas.drawAtlas(
image,
[
RSTransform.fromComponents(
rotation: -10,
scale: 1.0,
anchorX: 0,
anchorY: 0,
translateX: 10,
translateY: 10),
RSTransform.fromComponents(
rotation: 40,
scale: 1.0,
anchorX: 0,
anchorY: 0,
translateX: 90,
translateY: 90)
],
[Rect.fromLTWH(50, 50, 100, 100), Rect.fromLTWH(150, 150, 100, 100)],
[Colors.redAccent, Colors.blueAccent],
BlendMode.srcIn,
cullRect,
paint);
}
canvas.drawRawAtlas() ???待处理
画布裁剪
裁剪后,canvas的所有操作都限定在裁剪区域内
canvas.clipPath() 按指定路径裁剪
void _clipPath(Canvas canvas, Size size) {
Path path = Path();
path.lineTo(80, 80);
path.lineTo(-80, 80);
path.close();
canvas.clipPath(path);
canvas.drawPaint(new Paint()..color=Colors.redAccent);
}
canvas.clipRect() 按矩形裁剪
注意clipOp参数:
-
ClipOp.intersect 裁剪画布 内
-
ClipOp.difference 裁剪画布 外
void _clipRect(Canvas canvas, Size size) {
var rect = Rect.fromCenter(center: Offset.zero,width: 180,height: 120);
canvas.clipRect(rect,doAntiAlias: true,clipOp: ui.ClipOp.intersect); // 裁剪画布 内
// canvas.clipRect(rect,doAntiAlias: true,clipOp: ui.ClipOp.difference); // 裁剪画布 外
canvas.drawPaint(new Paint()..color=Colors.redAccent);
}
canvas.clipDRect() 按圆角矩形裁剪
void _clipDRect(Canvas canvas, Size size) {
var rect = Rect.fromCenter(center: Offset.zero,width: 180,height: 120);
canvas.clipRRect(RRect.fromRectAndRadius(rect, Radius.circular(30)),doAntiAlias: true); //
canvas.drawPaint(new Paint()..color=Colors.redAccent);
}
画布的层
save(),saveLayer(),restore()
- canvas.save(),保存之前画的内容与canvas的状态
- canvas.saveLayer(rect,paint),保存之前画的内容,并新建一个图层,以后在此新图层上绘制。参数rect确定图层的范围,只能在此范围内绘制,超出范围的绘制不显示。参数paint表示其blendMode与之前的图层的混合模式。
- canvas.restore(),将该方法与前面最近的一个save() 或saveLayer()之间的操作合并到一起,save与restore,saveLayer与restore是成对儿出现的,只有save没有restore,是会报错的。
void test10(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
canvas.drawPaint(paint);// 整个区域
paint ..color = Colors.blue[200];
canvas.drawRect(Offset.zero&size, paint);// 绘制区域
paint ..color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(200,200),radius:100), paint);
// 保存之前的绘制内容
canvas.save();
// 接着绘制一个矩形,是在前面的图层上继续绘制的
paint.color = Colors.grey;
canvas.drawRect(Rect.fromCircle(center:Offset(250,300),radius:100), paint);
// 保存
canvas.restore();
// 设定混合模式
paint.blendMode = BlendMode.src;
//保存之前并新建一个图层,并指定新图层的区域与混合模式
canvas.saveLayer(Rect.fromCircle(center:Offset(270,350),radius:100), paint);
paint ..color = Colors.orange;
//在新的图层上画一个矩形,设定矩形范围超过图层范围
canvas.drawRect(Rect.fromCircle(center:Offset(270,350),radius:150), paint);
// 保存
canvas.restore();
}
可见,在新的图层上画一个超出图层范围的矩形,超出范围的内容没有显示,只有图层区域内的显示了出来
canvas.getSaveCount() 获取save栈中的层数
每save()或saveLayer()一次 getSaveCount() 取值 +1;
每restore()一次,getSaveCount() 取值 -1;
如:
print("canvas1="+canvas.getSaveCount().toString());// canvas1=1
canvas.save();
canvas.save();
print("canvas2="+canvas.getSaveCount().toString());// canvas2= 3
canvas.restore();
print("canvas3="+canvas.getSaveCount().toString());// canvas3= 2
canvas.restore();
print("canvas4="+canvas.getSaveCount().toString());// canvas4= 1
画布变换
canvas.translate()平移
canvas.translate(dx,dy)
指的是整个canvas向xy分别平移dx dy的距离
// 平移
void test11(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 初始位置
canvas.drawRect(Offset.zero&size/2, paint);// 绘制区域
paint ..color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存之前的绘制内容
canvas.save();
canvas.translate(200, 200);//向xy200平移
paint.color = Colors.red[200];
canvas.drawRect(Offset.zero&size/2, paint);// 绘制canvas区域
paint.color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存
canvas.restore();
}
平移后,canvas绘制的内容显示在平移后的位置。
canvas.scale(sx,sy)缩放
canvas.scale(0.5,2):x轴缩小为一半,y轴放大为2倍
看效果:
// 缩放
void test12(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 初始位置
canvas.drawRect(Offset.zero&size/2, paint);// 绘制区域
paint ..color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存之前的绘制内容
canvas.save();
canvas.scale(0.5,0.5);// 缩小为原来的一半
paint.color = Colors.red[200];
canvas.drawRect(Offset.zero&size/2, paint);// 绘制canvas区域
paint.color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存
canvas.restore();
}
canvas.rotate(value)旋转
- value: 取值pi=3.14,顺时针旋转180读,取值pi *2 顺时针旋转360度。
- 旋转的圆心是canvas的左顶点,即(0,0)
看效果:
先平移,再旋转:
// 旋转
void test13(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 初始位置
canvas.drawRect(Offset.zero&size/2, paint);// 绘制区域
paint ..color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存之前的绘制内容
canvas.save();
canvas.translate(200, 200);//向xy200平移
canvas.rotate(1);// 旋转1弧度
paint.color = Colors.red[200];
canvas.drawRect(Offset.zero&size/2, paint);// 绘制canvas区域
paint.color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存
canvas.restore();
}
canvas.skew(sx,sy)斜切
取值为tan(sx) tan(sy)
// 斜切
void test14(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.orange[200]
..style = PaintingStyle.fill
..isAntiAlias = true;
// 初始位置
canvas.drawRect(Offset.zero&size/2, paint);// 绘制区域
paint ..color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存之前的绘制内容
canvas.save();
canvas.skew(pi/4, 0);//
// canvas.skew(tan(45), 0);//
paint.color = Colors.red[200];
canvas.drawRect(Offset.zero&size/2, paint);// 绘制canvas区域
paint.color = Colors.green[200];
canvas.drawRect(Rect.fromCircle(center:Offset(100,100),radius:50), paint);// 画一个矩形
// 保存
canvas.restore();
}
canvas.transform() 矩阵变换
上面四种画布变换,最终都都是基于transform的。
void _transform(Canvas canvas, Size size) {
Path path = Path();
path.lineTo(60, 60);
path.lineTo(-60, 60);
path.lineTo(60, -60);
path.lineTo(-60, -160);
path.close();
canvas.drawPath(path, _paint);
// canvas.translate(140, 0);
canvas.transform(Float64List.fromList([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
120, 0, 0, 1]));
canvas.drawPath(
path,
_paint
..style = PaintingStyle.stroke
..strokeWidth = 2);
}