孙群 点击打开链接
Canvas绘图中牵扯到两种坐标系:Canvas坐标系与绘图坐标系。
-
Canvas坐标系
Canvas坐标系指的是Canvas本身的坐标系,Canvas坐标系有且只有一个,且是唯一不变的,其坐标原点在View的左上角,从坐标原点向右为x轴的正半轴,从坐标原点向下为y轴的正半轴。 -
绘图坐标系
Canvas的drawXXX方法中传入的各种坐标指的都是绘图坐标系中的坐标,而非Canvas坐标系中的坐标。默认情况下,绘图坐标系与Canvas坐标系完全重合,即初始状况下,绘图坐标系的坐标原点也在View的左上角,从原点向右为x轴正半轴,从原点向下为y轴正半轴。但不同于Canvas坐标系,绘图坐标系并不是一成不变的,可以通过调用Canvas的translate方法平移坐标系,可以通过Canvas的rotate方法旋转坐标系,还可以通过Canvas的scale方法缩放坐标系,而且需要注意的是,translate、rotate、scale的操作都是基于当前绘图坐标系的,而不是基于Canvas坐标系,一旦通过以上方法对坐标系进行了操作之后,当前绘图坐标系就变化了,以后绘图都是基于更新的绘图坐标系了。也就是说,真正对我们绘图有用的是绘图坐标系而非Canvas坐标系。
private void drawText(Canvas canvas){
int canvasWidth = canvas.getWidth();
int halfCanvasWidth = canvasWidth / 2;
float translateY = textHeight;
//绘制正常文本
canvas.save();
canvas.translate(0, translateY);
canvas.drawText("正常绘制文本", 0, 0, paint);
canvas.restore();
translateY += textHeight * 2;
//绘制绿色文本
paint.setColor(0xff00ff00);//设置字体为绿色
canvas.save();
canvas.translate(0, translateY);//将画笔向下移动
canvas.drawText("绘制绿色文本", 0, 0, paint);
canvas.restore();
paint.setColor(0xff000000);//重新设置为黑色
translateY += textHeight * 2;
//设置左对齐
paint.setTextAlign(Paint.Align.LEFT);//设置左对齐
canvas.save();
canvas.translate(halfCanvasWidth, translateY);
canvas.drawText("左对齐文本", 0, 0, paint);
canvas.restore();
translateY += textHeight * 2;
//设置居中对齐
paint.setTextAlign(Paint.Align.CENTER);//设置居中对齐
canvas.save();
canvas.translate(halfCanvasWidth, translateY);
canvas.drawText("居中对齐文本", 0, 0, paint);
canvas.restore();
translateY += textHeight * 2;
//设置右对齐
paint.setTextAlign(Paint.Align.RIGHT);//设置右对齐
canvas.save();
canvas.translate(halfCanvasWidth, translateY);
canvas.drawText("右对齐文本", 0, 0, paint);
canvas.restore();
paint.setTextAlign(Paint.Align.LEFT);//重新设置为左对齐
translateY += textHeight * 2;
//设置下划线
paint.setUnderlineText(true);//设置具有下划线
canvas.save();
canvas.translate(0, translateY);
canvas.drawText("下划线文本", 0, 0, paint);
canvas.restore();
paint.setUnderlineText(false);//重新设置为没有下划线
translateY += textHeight * 2;
//绘制加粗文字
paint.setFakeBoldText(true);//将画笔设置为粗体
canvas.save();
canvas.translate(0, translateY);
canvas.drawText("粗体文本", 0, 0, paint);
canvas.restore();
paint.setFakeBoldText(false);//重新将画笔设置为非粗体状态
translateY += textHeight * 2;
//文本绕绘制起点顺时针旋转
canvas.save();
canvas.translate(0, translateY);
canvas.rotate(20);
canvas.drawText("文本绕绘制起点旋转20度", 0, 0, paint);
canvas.restore();
}
我们在上面的代码中将canvas.translate()和canvas.rotate()放到了canvas.save()和canvas.restore()之间,这样做的好处是,在canvas.save()调用时,将当前坐标系保存下来,将当前坐标系的矩阵Matrix入栈保存,然后通过translate或rotate等对坐标系进行变换,然后进行绘图,绘图完成后,我们通过调用canvas.restore()将之前保存的Matrix出栈,这样就将当前绘图坐标系恢复到了canvas.save()执行的时候状态。如果熟悉OpenGL开发,对这种模式应该很了解。
private void drawArc(Canvas canvas){
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int count = 5;
float ovalHeight = canvasHeight / (count + 1);
float left = 10 * density;
float top = 0;
float right = canvasWidth - left;
float bottom= ovalHeight;
RectF rectF = new RectF(left, top, right, bottom);
paint.setStrokeWidth(2 * density);//设置线宽
paint.setColor(0xff8bc5ba);//设置颜色
paint.setStyle(Paint.Style.FILL);//默认设置画笔为填充模式
//绘制用drawArc绘制完整的椭圆
canvas.translate(0, ovalHeight / count);
canvas.drawArc(rectF, 0, 360, true, paint);
//绘制椭圆的四分之一,起点是钟表的3点位置,从3点绘制到6点的位置
canvas.translate(0, (ovalHeight + ovalHeight / count));
canvas.drawArc(rectF, 0, 90, true, paint);
//绘制椭圆的四分之一,将useCenter设置为false
canvas.translate(0, (ovalHeight + ovalHeight / count));
canvas.drawArc(rectF, 0, 90, false, paint);
//绘制椭圆的四分之一,只绘制轮廓线
paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
canvas.translate(0, (ovalHeight + ovalHeight / count));
canvas.drawArc(rectF, 0, 90, true, paint);
//绘制带有轮廓线的椭圆的四分之一
//1. 先绘制椭圆的填充部分
paint.setStyle(Paint.Style.FILL);//设置画笔为填充模式
canvas.translate(0, (ovalHeight + ovalHeight / count));
canvas.drawArc(rectF, 0, 90, true, paint);
//2. 再绘制椭圆的轮廓线部分
paint.setStyle(Paint.Style.STROKE);//设置画笔为线条模式
paint.setColor(0xff0000ff);//设置轮廓线条为蓝色
canvas.drawArc(rectF, 0, 90, true, paint);
}
对canvas进行变换是会叠加的。
所以为了绘图坐标系要向每次和Canvas坐标系重合,需要在对canvas变换前保存图层,完成后恢复图层。
如果为了一致叠加前面的变换,那就没必要保存再恢复canvas了。
文字是从文字的左下角开始绘制的。
如图: