上一节记录了Canvas
绘制图形,都是一些最基础的知识,但是复杂的效果也都是基础知识慢慢垒砌起来的不是吗。下面要看的是Canvas
里边的画布的操作。GcsSloop大神举的例子很好,当我们想要画一条倾斜30度的直线,该怎么做呢?要通过三角函数去计算起始点的坐标值,这个方法有点麻烦。那我们能不能把画布倾斜30度呢?换个角度思考,Canvas
也给了我们通过这条途径上边的一系列方法。
首先我们要记住画布操作的一些要注意的点
画布的操作只对后边的操作起作用,不会对前边的操作起作用。
这句话怎么解释呢?比如说,我们先画一条横线,坐标点(30,30)->(60,30);然后我们将画布向右平移40个单位值;再画一条坐标点(30,30)->(60,30)的横线。整个操作三步,画横线,平移,再画横线。最后我们看到的的结果是(30,30)->(60,30)一条线,(70,30)->(100,30)一条线。其中前边一条线没有因为画布的平移而改变位置。又臭又长的说了一段,反正我自己给自己解释通了。
画布操作分两种:一种是保存回滚一类的操作,一种是平移缩放之类的操作。先讲后边一种。
平移(translate)
canvas.translate(x,y);
画布的位移操作是基于坐标系的位移,怎么理解这句话呢?从上边的例子我们大概就有点认识了,其实位移是将整个坐标系平移,也就是说将坐标原点换换位置,上边我们平移40,其实就是将坐标远点平移40,之前的(40,0),平移之后的坐标就是(0,0)。
画布的平移是可叠加的,第一次平移30,第二次平移40,相当于平移了70。
缩放(scale)
canvas.scale(sx,sy);
canvas.scale(sx,sy,px,py);
第一个方法的两个参数分别为x和y的缩放比例,第二个方法多出来的两个参数是修改后的缩放原点。默认缩放原点是坐标原点,第二种方法可以重设置缩放的原点。
和平移一样,缩放也是可以叠加的。
当缩放的数值为负数的时候,为根据中心轴翻转并缩放,中心轴就是坐标轴。
缩放比例(sx,sy)取值范围对应的说明:
取值范围(n) | 说明 |
---|---|
(-∞, -1) | 先根据缩放中心放大n倍,再根据中心轴进行翻转 |
-1 | 根据缩放中心轴进行翻转 |
(-1, 0) | 先根据缩放中心缩小到n,再根据中心轴进行翻转 |
0 | 不会显示,若sx为0,则宽度为0,不会显示,sy同理 |
(0, 1) | 根据缩放中心缩小到n |
1 | 没有变化 |
(1, +∞) | 根据缩放中心放大n倍 |
默认的缩放中心是坐标原点,缩放中心轴是坐标轴。
// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);
RectF rect = new RectF(0,-400,400,0); // 矩形区域
mPaint.setColor(Color.BLACK); // 绘制黑色矩形
canvas.drawRect(rect,mPaint);
canvas.scale(0.5f,0.5f); // 画布缩放
mPaint.setColor(Color.BLUE); // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);
下面一个例子可能有点复杂,需要好好看一下。
第二种方法后边两个参数是修改缩放中心的,当我们修改缩放中心进行缩放之后,在进行普通的缩放,那么最后一次的缩放中心会在哪呢?
//将坐标原点移动到画布中心
canvas.translate(mWidth / 2, mHeight / 2);
Rect rect = new Rect(0, -400, 400, 0);
canvas.drawRect(rect, paint);
//改变缩放中心至 200,0
canvas.scale(0.5f, 0.5f,200,0);
paint.setColor(Color.RED);
canvas.drawRect(rect, paint);
//调用普通缩放
canvas.scale(0.5f, 0.5f);
paint.setColor(Color.RED);
canvas.drawRect(rect, paint);
//调用普通缩放,并沿缩放中心轴旋转
canvas.scale(-0.5f, -0.5f);
paint.setColor(Color.RED);
canvas.drawRect(rect, paint);
paint.setColor(Color.RED);
canvas.drawRect(rect, paint);
我们看到,第一次缩放是我能想到的,缩放中心确实向右移动了200,但是第三次缩放,我们去掉了缩放中的指定,第二次缩放中心应该在第1个原点啊。可是为什么跑到了第3个原点位置呢?原因是当你缩放操作的时候,将画布整体做小,整个坐标系也会跟着缩小。原来的100,缩小0.5之后,就是50。经过缩放之后,远点已经被缩放到3的位置。
其实这个结论,在缩放的时候一直存在,因为rect一直没变过尺寸,但是每次缩放,大小都会变。
缩放操作是缩放整个坐标系,将坐标系缩放(sx,sy)。
旋转(rotate)
canvas.rotate(rd);
canvas.rotate(rd,rpx,rpy);
里边的参数都是float
类型。
和缩放一样,第二种多出来的两个参数是控制旋转的中心点。
默认的旋转中心依然是坐标原点。
旋转也是可以叠加的。
错切(skew)
skew翻译过来就是歪斜,偏离,斜交。
错切只有一个方法:
canvas.skew(sx,sy);
参数的含义:
float sx:将画布再x方向上倾斜相应的角度,sx是倾斜角度的tan值;
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值。
变换之后的:
x=x+sxy
y=syx+y
// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);
RectF rect = new RectF(0,0,200,200); // 矩形区域
mPaint.setColor(Color.BLACK); // 绘制黑色矩形
canvas.drawRect(rect,mPaint);
canvas.skew(1,0); // 水平错切 <- 45度
mPaint.setColor(Color.BLUE); // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);
错切也是可以叠加的,但是错切的舒徐不同,结果也会不同。
先水平,后垂直:
// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);
RectF rect = new RectF(0,0,200,200); // 矩形区域
mPaint.setColor(Color.BLACK); // 绘制黑色矩形
canvas.drawRect(rect,mPaint);
canvas.skew(1,0); // 水平错切
canvas.skew(0,1); // 垂直错切
mPaint.setColor(Color.BLUE); // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);
先垂直,后水平:
//将坐标原点移动到画布中心
canvas.translate(mWidth / 2, mHeight / 2);
Rect rect = new Rect(0, 0, 200, 200);
canvas.drawRect(rect, paint);
paint.setColor(Color.RED);
//先垂直错切,后水平错切 结果在y轴被拉伸的多一些
canvas.skew(0,1);
canvas.skew(1,0);
canvas.drawRect(rect,paint);
先垂直,后水平:在y轴上被拉伸的多一些;
先水平,后垂直:在x轴上被拉伸的多一些。
这些都是显而易见的结果,可以通过上边的公式计算出来。
上边的canvas
的变换操作就这四个,不论怎么变化,错切也好,位移也罢,其实都是对canvas
(画布)进行的操作,从来没有改变过绘制的图形。矩形还是那个矩形,只不过画到一张画布上的时候,把画布进行了操作,那画布上便的图形也会跟着变化了。
画布的保存和回滚:
其实这里边的东西还是有点多的,也有点绕,但是我们现在只需要记住最常用的操作就好了:
canvas.save();//保存状态;
.... //具体操作
canvas.restore();//回滚到上一次保存的状态。
....
具体操作一下。
比如上边的错切例子,先进行先水平后垂直错切,再画一个先垂直后水平错切。该怎么画呢?想一下,是不是直接上去,画完第一个图之后,紧接着初始化一下画笔,再接着画?我刚开始也是这样搞得,画出来的。。。。不是我想要的样子。因为你做了第一步操作的时候,画布已经被你拉扯的变大了,你再上去画,还是在变化之后的画布上继续进行拉扯。
那我们想要画出两种不同错切的对比,该怎么操作呢?首先我们画完第一张图的时候,应该将画布进行还原,还原到上一次的状态,就是canvas.restore();
操作。这个操作有个前提,就是在第一次错切的时候,我们要首先保存一下状态canvas.save();
,下次还原的时候,我们才知道要还原到那个状态。不然只写canvas.restore();
是没用的。
canvas.save();//保存状态;
drawSkew1(canvas);第一次错切
canvas.restore();回滚到上一次状态,这里是将画布还原。
drawSkew2(canvas);第二次错切。
结果就是上边那张图。
其他的关于画布保存,增加图层的等等操作,可以参考大神的博客,我不想写了。安卓自定义View进阶-Canvas之画布操作