Android画图Path的使用
/** * Paint类介绍 * * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色, * 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法, * 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。 * * 1.图形绘制 * setARGB(int a,int r,int g,int b); * 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。 * * setAlpha(int a); * 设置绘制图形的透明度。 * * setColor(int color); * 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。 * * setAntiAlias(boolean aa); * 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。 * * setDither(boolean dither); * 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰 * * setFilterBitmap(boolean filter); * 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示 * 速度,本设置项依赖于dither和xfermode的设置 * * setMaskFilter(MaskFilter maskfilter); * 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等 * * setColorFilter(ColorFilter colorfilter); * 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果 * * setPathEffect(PathEffect effect); * 设置绘制路径的效果,如点画线等 * * setShader(Shader shader); * 设置图像效果,使用Shader可以绘制出各种渐变效果 * * setShadowLayer(float radius ,float dx,float dy,int color); * 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色 * * setStyle(Paint.Style style); * 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE * * setStrokeCap(Paint.Cap cap); * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式 * Cap.ROUND,或方形样式Cap.SQUARE * * setSrokeJoin(Paint.Join join); * 设置绘制时各图形的结合方式,如平滑效果等 * * setStrokeWidth(float width); * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度 * * setXfermode(Xfermode xfermode); * 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果 * * 2.文本绘制 * setFakeBoldText(boolean fakeBoldText); * 模拟实现粗体文字,设置在小字体上效果会非常差 * * setSubpixelText(boolean subpixelText); * 设置该项为true,将有助于文本在LCD屏幕上的显示效果 * * setTextAlign(Paint.Align align); * 设置绘制文字的对齐方向 * * setTextScaleX(float scaleX); * 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果 * * setTextSize(float textSize); * 设置绘制文字的字号大小 * * setTextSkewX(float skewX); * 设置斜体文字,skewX为倾斜弧度 * * setTypeface(Typeface typeface); * 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等 * * setUnderlineText(boolean underlineText); * 设置带有下划线的文字效果 * * setStrikeThruText(boolean strikeThruText); * 设置带有删除线的效果 * */
private class MyView2 extends View { public MyView2(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.WHITE); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE);//设置为空心 paint.setStrokeWidth(3); canvas.drawCircle(40, 40, 30, paint); canvas.drawRect(10, 90, 70, 150, paint); canvas.drawRect(10, 170, 70, 200, paint); canvas.drawOval(new RectF(10, 220, 70, 250), paint); Path path = new Path();//三角形 path.moveTo(10, 330); path.lineTo(70, 330); path.lineTo(40, 270); path.close(); canvas.drawPath(path, paint); Path path1 = new Path();//梯形 path1.moveTo(10, 410);//绘画基点 path1.lineTo(70, 410); path1.lineTo(55, 350); path1.lineTo(25, 350); path1.close();//把开始的点和最后的点连接在一起,构成一个封闭图形 /* * 最重要的就是movtTo和close,如果是Style.FILL的话,不设置close,也没有区别,可是如果是STROKE模式, * 如果不设置close,图形不封闭。 * * 当然,你也可以不设置close,再添加一条线,效果一样。 */ canvas.drawPath(path1, paint); ///第二列 paint.setColor(Color.BLUE); paint.setStyle(Paint.Style.FILL);//设置实心 canvas.drawCircle(120, 40, 30, paint); canvas.drawRect(90, 90, 150, 150, paint); canvas.drawRect(90, 170, 150, 200, paint); RectF re2 = new RectF(90, 220, 150, 250); canvas.drawOval(re2, paint); Path path2 = new Path(); path2.moveTo(90, 330); path2.lineTo(150, 330); path2.lineTo(120, 270); path2.close(); canvas.drawPath(path2, paint); Path path3 = new Path(); path3.moveTo(90, 410); path3.lineTo(150, 410); path3.lineTo(135, 350); path3.lineTo(105, 350); path3.close(); canvas.drawPath(path3, paint); 第三列 /* * LinearGradient shader = new LinearGradient(0, 0, endX, endY, new * int[]{startColor, midleColor, endColor},new float[]{0 , 0.5f, * 1.0f}, TileMode.MIRROR); * 参数一为渐变起初点坐标x位置,参数二为y轴位置,参数三和四分辨对应渐变终点 * 其中参数new int[]{startColor, midleColor,endColor}是参与渐变效果的颜色集合, * 其中参数new float[]{0 , 0.5f, 1.0f}是定义每个颜色处于的渐变相对位置, 这个参数可以为null,如果为null表示所有的颜色按顺序均匀的分布 */ Shader mShader = new LinearGradient(0, 0, 100, 100, new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW }, null, Shader.TileMode.REPEAT); // Shader.TileMode三种模式 // REPEAT:沿着渐变方向循环重复 // CLAMP:如果在预先定义的范围外画的话,就重复边界的颜色 // MIRROR:与REPEAT一样都是循环重复,但这个会对称重复 paint.setShader(mShader);// 用Shader中定义定义的颜色来话 canvas.drawCircle(200, 40, 30, paint); canvas.drawRect(170, 90, 230, 150, paint); canvas.drawRect(170, 170, 230, 200, paint); RectF re3 = new RectF(170, 220, 230, 250); canvas.drawOval(re3, paint); Path path4 = new Path(); path4.moveTo(170, 330); path4.lineTo(230, 330); path4.lineTo(200, 270); path4.close(); canvas.drawPath(path4, paint); Path path5 = new Path(); path5.moveTo(170, 410); path5.lineTo(230, 410); path5.lineTo(215, 350); path5.lineTo(185, 350); path5.close(); canvas.drawPath(path5, paint); //第4列 paint.setTextSize(24); canvas.drawText("圆形", 240, 50, paint); canvas.drawText("正方形", 240, 120, paint); canvas.drawText("长方形", 240, 190, paint); canvas.drawText("椭圆形", 240, 250, paint); canvas.drawText("三角形", 240, 320, paint); canvas.drawText("梯形", 240, 390, paint); } }
Path常用方法
方法 | 作用 | 备注 |
---|---|---|
moveTo | 移动起点 | 移动下一次操作的起点位置 |
lineTo | 连接直线 | 连接上一个点到当前点之间的直线 |
setLastPoint | 设置终点 | 重置最后一个点的位置 |
close | 闭合路劲 | 从最后一个点连接最初的一个点,形成一个闭合区域 |
addRect | 添加矩形 | 添加矩形到当前Path |
addRoundRect | 添加圆角矩形 | 添加圆角矩形到当前Path |
addOval | 添加椭圆 | 添加椭圆到当前Path |
addCircle | 添加圆 | 添加圆到当前Path |
addPah | 添加路劲 | 添加路劲到当前Path |
addArc | 添加圆弧 | 添加圆弧到当前Path |
arcTo | 圆弧 | 绘制圆弧,注意和addArc的区别 |
isEmpty | 是否为空 | 判定Path是否为空 |
isRect | 是否为矩形 | 判定Path是否是一个矩形 |
set | 替换路劲 | 用新的路劲替换当前路劲的所有内容 |
offset | 偏移路劲 | 对当前的路劲进行偏移 |
quadTo | 贝塞尔曲线 | 二次贝塞尔曲线的方法 |
cubicTo | 贝塞尔曲线 | 三次贝塞尔曲线的方法 |
rMoveTo,rlineTo,rQuadTo,rCubicTo | rXxx方法 | 不带r的方法是基于原点坐标系(偏移量),带r的基于当前点坐标系(偏移量) |
op | 布尔操作 | 对两个Path进行布尔运算(交集,并集)等操作 |
setFillType | 填充模式 | 设置Path的填充模式 |
getFillType | 填充模式 | 获取Path的填充 |
isInverseFillType | 是否逆填充 | 判断是否是逆填充模式 |
toggleInverseFillType | 相反模式 | 切换相反的填充模式 |
getFillType | 填充模式 | 获取Path的填充 |
incReserve | 提示方法 | 提示Path还有多少个点等待加入 |
computeBounds | 计算边界 | 计算Path的路劲 |
reset,rewind | 重置路劲 | 清除Path中的内容(reset相当于new Path , rewind 会保留Path的数据结构) |
transform | 矩阵操作 | 矩阵变换 |
Path方法使用详解
使用Path
不仅可以绘制简单的图形(如圆形,矩形,直线等),也可以绘制复杂一些的图形(如正多边形,五角星等),还有绘制裁剪和绘制文本都会用到Path
。由于方法比较多,我这里分组来讲下。
moveTo , lineTo , setLastPoint , close
先创建画笔:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
注意paint.setStyle(Paint.Style.FILL);
,设置画笔为实心。一些线条将在画布上看不见。
1、lineTo
首先我们来看看lineTo
,如果你直接moveTo
将看不出效果。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
为了方便大家好观察坐标的变化,我在屏幕上画出了网格,每块网格的宽高都是100。由于第一次之前没有过操作,所以默认点就是原点(屏幕左上角),第一次lineTo
就是坐标原点到(200,200)之间的直线。第二次lineTo
就是上一次结束点位置(200,200)到(400,0)点之间的直线。
2、moveTo 和setLastPoint
方法预览:
- 1
- 2
- 3
- 1
- 2
- 3
这两个方法在作用上有相似之处,却是两个不同的东西,具体参考下表:
方法名 | 作用 | 是否影响之前的操作 | 是否影响之后的操作 |
---|---|---|---|
moveTo | 移动下一次操作的起点位置 | 否 | 是 |
setLastPoint | 改变上一次操作点的位置 | 是 | 是 |
来看看下面的例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
效果图:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
效果图:
当我们绘制线条之前,调用moveTo
和 setLastPoint
效果是一样的,都是对坐标原点(0,0)进行操作。setLastPoint
是重置上一次操作的最后一点,在执行完第一次lineTo
的时候,最后一个点就是(200,200),setLastPoint
更改(200,200)为(300,100),所以在执行的时候就是(300,100)到(400, 0)之间的连线了。
3、close
方法预览
- 1
- 1
close
方法连接最后一个点和最初一个点(如果两个点不重合)形成一个闭合的图形。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
效果图:
上图中可以看到lineTo(500,100)
直线和lineTo(300,400)
直线,而close
方法就是连接(300,400),(100,100)两点,形成一个闭合的区域。
注意:close的作用的封闭路径,如果连接最后一个点和最初一个点任然无法形成闭合的区域,那么close什么也不做。
quadTo,cubicTo
二次贝塞尔曲线以及三次贝塞尔曲线。
1、quadTo
方法预览
- 1
- 1
quadTo
方法其中 (x1,y1) 为控制点,(x2,y2)为结束点。
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
效果图:
2、cubicTo
方法预览:
- 1
- 1
cubicTo
方法比quadTo
方法多了一个点坐标,那么其中(x1,y1) 为控制点,(x2,y2)为控制点,(x3,y3) 为结束点。
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
绘制的图形和上面的quadTo
绘制的图形是一样的。我们去掉moveTo
来看看运行的效果图:
如果你想了解贝塞尔曲线公式,请链接这里
addXxx和arcTo
主要是向Path
中添加基本图形以及区分addArc
和arcTo
1、添加基本图形
方法预览:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
我们仔细观察上面的方法,在最后都有一个Path.Direction
,这是个什么东东呢?Direction
的意思是方向,指导,趋势。点进去跟一下你会发现Direction
是一个枚举类型(Enum)分别有CW(顺时针),CCW(逆时针)两个常量。那么它的作用主要有以下两点:
序号 | 作用 |
---|---|
1 | 在添加图形时确定闭合顺序(各个点的记录顺序) |
2 | 对自相交图形的渲染结果有影响 |
我们先来看看闭合顺序的问题,添加一个矩形看看:
- 1
- 2
- 3
- 1
- 2
- 3
我将上面的代码CW
改成CCW
再运行一次,结果一模一样,尼玛区别在哪里啊。稍安勿躁,想看到区别就要用到setLastPoint
(重置最后一个点的坐标)。我们来这样变变代码:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
效果立马现行:
为什么图形会发生奇怪的变化呢。我们先来分析一下,绘制一个矩形至少需要对角线的两个点,根据这两个点计算出四条边然后把四条边按照顺序连接起来。上图的起始坐标是(100,200)按着顺时针的方向连接(500,200),(500,400),(100,400)最后连接(100,200)形成一个矩形。setLastPoint
是重置上一个操作点坐标及改变(100,400)为(200,400),所以出现了上图的效果。
接下来我们看看逆时针的情况:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
效果图:
我们理清楚了闭合的问题,相交问题与设置填充模式有关。
我以addCircle
方法来讲解添加图形,并不会逐一讲解。
- 1
- 2
- 3
- 1
- 2
- 3
绘制圆形,(300,300)点表示圆心坐标,200 表示半径长度。
2、addPath
方法预览:
- 1
- 2
- 3
- 1
- 2
- 3
addPath方法就是将两个路径合并到一起。第二个方法的dx,dy
指的是偏移量,第三个方法是添加到当前path之前先使用Matrix进行变换。来看看下面例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
效果图:
3、addArc与arcTo
方法预览:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
从方法名字上面看,这两个方法都是与圆弧有关,那么他们之间肯定是有区别的:
名称 | 作用 | 区别 |
---|---|---|
addArc | 添加一个圆弧到Path | 直接添加一个圆弧到path中,和上一次操作点无关 |
arcTo | 添加一个圆弧到Path | 添加一个圆弧到path中,如果圆弧的起点和上次操作点坐标不同就连接两个点 |
startAngle
表示开始圆弧度数(0度与X轴方向对齐,顺时针移动,弧度增大)。
注意:sweepAngle
表示运动了多少弧度,并不是结束弧度。
forceMoveTo表示“是否强制使用moveTo”,也就是说是否使用moveTo将上一次操作点移动到圆弧的起点坐标。默认是false。
forceMoveTo | 含义 |
---|---|
true | 将最后一个点移动到圆弧起点,即不连接最后一个点与圆弧起点 |
false | 不移动,而是连接最后一个点与圆弧起点(注意之前没有操作的话,不会连接原点) |
示例:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
效果图:
我们把 path.arcTo(rectF, 0, 270, true);
改成 path.arcTo(rectF, 0, 270, false);
,来看看效果图:
从上面两张图可以看出明显的变化。
isEmpty、 isRect、 set 和 offset
isEmpty
判断path中是否包含内容。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
isRect
方法预览:
- 1
- 1
判断path是否是一个矩形,如果是一个矩形的话,会将矩形的信息存放进参数rect中。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
set
方法预览:
- 1
- 1
将新的path赋值到现有path。相当于运算符中的“=”,如a=b
,把b
赋值给a
还是一起来看个例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
效果图:
offset
方法预览:
- 1
- 2
- 1
- 2
这个方法就是对Path
进行一段平移,正方向和X轴,Y轴方向一致(如果dx为正数则向右平移,反之向左平移;如果dy为正则向下平移,反之向上平移)。我们看到第二个方法多了一个dst
,这个又是一个什么玩意呢,其实参数das是存储平移后的path的。
用例子来说明一下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
效果图:
从运行效果图可以看出,虽然我们在dst中添加了一个圆形,但是并没有表现出来,所以,当dst中存在内容时,dst中原有的内容会被清空,而存放平移后的path。
FillType
方法预览:
- 1
- 2
- 1
- 2
setFillType
方法中的参数Path.FillType
为枚举类型:
FillType值 | 含义 |
---|---|
FillType.WINDING | 取path所有所在区域 默认值 |
FillType.EVEN_ODD | 取path所在并不相交区域 |
FillType.INVERSE_WINDING | 取path所有未占区域 |
FillType.INVERSE_EVEN_ODD | 取path未占或相交区域 |
path所在区域
path所在区域
path所在区域
setFillType
WINDING
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
效果图:
EVEN_ODD
代码我就不贴了,直接上图(上mis):
INVERSE_WINDING
INVERSE_EVEN_ODD
isInverseFillType
是否是逆填充模式:WINDING 和 EVEN_ODD 返回false;
INVERSE_WINDING 和 INVERSE_EVEN_ODD 返回true;
toggleInverseFillType
切换相反的填充模式,如果填充模式为WINDING
则填充模式为INVERSE_WINDING
,反之为WINDING
模式;如果填充模式为EVEN_ODD
则填充模式为INVERSE_EVEN_ODD
,反之为EVEN_ODD
模式。
举个例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
效果图和上面EVEN_ODD
模式一模一样。
就写到这里,文中有什么写错,写偏的请给我留言。