在学习自定义view的时候,跟随https://hencoder.com/ui-1-1/的教程写写demo,遇到一个问题,需求是这样的,使用canvas的drawPath()方法绘制一个心形,如下图
教程给的代码是
public class PathView extends View {
Paint paint = new Paint();
Path path = new Path(); // 初始化 Path 对象
......
{
// 使用 path 对图形进行描述(这段描述代码不必看懂)
path.addArc(200, 200, 400, 400, -225, 225);
path.arcTo(400, 200, 600, 400, -180, 225, false);
path.lineTo(400, 542);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint); // 绘制出 path 描述的图形(心形),大功告成
}
}
后来自己尝试实现的时候,把图形分解了一下,如图
于是,很自然的按照这个步骤来绘制图形,首先 添加左边的弧
path.addArc(100,100,300,300,-225,225);
其次,添加右边的弧
path.addArc(300,100,500,300,180,225);
最后,连接到中间点
path.lineTo(300,450);
开始运行!
???
为什么会这样呢,lineTo不是会把图形没封闭的地方连接起来吗,怎么左边不连呢?
找了一会,发现跟源代码不一样的地方就是第二个弧形的绘制,我用了addArc() 方法,而源代码用的是arcTo()方法,问题肯定在这里,接下来就是找这两个方法的区别了,作者大佬对这两个方法有着如下解释:
另外,第二组还有两个特殊的方法:
arcTo()
和addArc()
。它们也是用来画线的,但并不使用当前位置作为弧线的起点。arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(RectF oval, float startAngle, float sweepAngle) 画弧形
这个方法和
Canvas.drawArc()
比起来,少了一个参数useCenter
,而多了一个参数forceMoveTo
。少了
useCenter
,是因为arcTo()
只用来画弧形而不画扇形,所以不再需要useCenter
参数;而多出来的这个forceMoveTo
参数的意思是,绘制是要「抬一下笔移动过去」,还是「直接拖着笔过去」,区别在于是否留下移动的痕迹。paint.setStyle(Style.STROKE); path.lineTo(100, 100); path.arcTo(100, 100, 300, 300, -90, 90, true); // 强制移动到弧形起点(无痕迹)
paint.setStyle(Style.STROKE); path.lineTo(100, 100); path.arcTo(100, 100, 300, 300, -90, 90, false); // 直接连线连到弧形起点(有痕迹)
addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) / addArc(RectF oval, float startAngle, float sweepAngle)
又是一个弧形的方法。一个叫
arcTo
,一个叫addArc()
,都是弧形,区别在哪里?其实很简单:addArc()
只是一个直接使用了forceMoveTo = true
的简化版arcTo()
。paint.setStyle(Style.STROKE); path.lineTo(100, 100); path.addArc(100, 100, 300, 300, -90, 90);
按这个理解,使用arcTo()方法,forceMoveTo参数值为true的时候,相当于直接将笔提起来,从另一个点开始绘制第二段弧,虽然这一段弧的起点与第一段弧有重叠的部分,但是,在计算机看来,这跟之前的弧形已经是不封闭的另一个图形了,因此,在接下来使用lineTo()方法将图形封闭的时候,封闭的只是第二段弧与中心点组成的图形,也就形成了我上面出现的不完整心形图案
为了证实上面的结论,我将第二段弧形的起始位置故意往右偏移了一小段,使它不与第一段弧相交,得到的图案如下:
path.addArc(100,100,300,300,-225,225);
path.arcTo(350,100,500,300,180,225,true);
可以看到,当forceMoveTo参数值为true时,整个图形其实是分成了两个封闭图形,之前由于两个图形相交,视觉上看不出来。
把forceMoveTo的值改为false,使“笔迹”不断,这样的话所有的图形连在一起,是一段封闭图形
path.addArc(100,100,300,300,-225,225);
path.arcTo(350,100,500,300,180,225,false);
最后,总结一下这个事的结论,
addArc()
只是一个直接使用了 forceMoveTo = true
的简化版 arcTo()
forceMoveTo的值决定图案是否连续,也就间接决定了调用path.close()时,封闭图形的连接
2019年11月4日18:05:48
解答问题:arcTo方法的最后一个参数forceMoveTo的含义是“是否强制移动绘制点”,如果这个值为true,代表
强制移动了当前绘制点的位置,这样的话后续绘制的图形与之前绘制的图形是分隔开的,调用path.close()方法
的时候不会连接之前绘制的点了;这个值为false的时候,会将接下来的绘制点与之前的图形最后一个绘制点连接起来,
这样的话,之前绘制的图形与之后绘制的图形还是一个整体,调用path.close()方法的时候会将未封闭的图形连接起来。
addArc()方法本质上是一个forceMoveTo参数值为true的arcTo()方法