自定义View(四)Canvas 画布操作

上一节记录了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=sy
x+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();//回滚到上一次保存的状态。
        ....

具体操作一下。


image.png

比如上边的错切例子,先进行先水平后垂直错切,再画一个先垂直后水平错切。该怎么画呢?想一下,是不是直接上去,画完第一个图之后,紧接着初始化一下画笔,再接着画?我刚开始也是这样搞得,画出来的。。。。不是我想要的样子。因为你做了第一步操作的时候,画布已经被你拉扯的变大了,你再上去画,还是在变化之后的画布上继续进行拉扯。
那我们想要画出两种不同错切的对比,该怎么操作呢?首先我们画完第一张图的时候,应该将画布进行还原,还原到上一次的状态,就是canvas.restore();操作。这个操作有个前提,就是在第一次错切的时候,我们要首先保存一下状态canvas.save();,下次还原的时候,我们才知道要还原到那个状态。不然只写canvas.restore();是没用的。

        canvas.save();//保存状态;
        drawSkew1(canvas);第一次错切
        canvas.restore();回滚到上一次状态,这里是将画布还原。
        drawSkew2(canvas);第二次错切。

结果就是上边那张图。
其他的关于画布保存,增加图层的等等操作,可以参考大神的博客,我不想写了。安卓自定义View进阶-Canvas之画布操作

你可能感兴趣的:(自定义View(四)Canvas 画布操作)