美文网首页自定义view
自定义view进阶篇《三》——Canvas之画布操作

自定义view进阶篇《三》——Canvas之画布操作

作者: 吕侯爷 | 来源:发表于2017-04-08 23:01 被阅读0次

    一、Canvas的常用操作速查表

    二、Canvas基本操作

    1.画布操作

    为什么要有画布操作?
    画布操作可以帮助我们用更加容易理解的方式制作图形。
    例如: 从坐标原点为起点,绘制一个长度为20dp,与水平线夹角为30度的线段怎么做?

    按照我们通常的想法(被常年训练出来的数学思维),就是先使用三角函数计算出线段结束点的坐标,然后调用drawLine即可。

    然而这是否是被固有思维禁锢了?
    假设我们先绘制一个长度为20dp的水平线,然后将这条水平线旋转30度,则最终看起来效果是相同的,而且不用进行三角函数计算,这样是否更加简单了一点呢?

    合理的使用画布操作可以帮助你用更容易理解的方式创作你想要的效果,这也是画布操作存在的原因。

    PS: 所有的画布操作都只影响后续的绘制,对之前已经绘制过的内容没有影响。

    ⑴位移(translate)

    translate是坐标系的移动,可以为图形绘制选择一个合适的坐标系。 请注意,位移是基于当前位置移动,而不是每次基于屏幕左上角的(0,0)点移动,如下:

    // 省略了创建画笔的代码
    
    // 在坐标原点绘制一个黑色圆形
    mPaint.setColor(Color.BLACK);
    canvas.translate(200,200);
    canvas.drawCircle(0,0,100,mPaint);
    
    // 在坐标原点绘制一个蓝色圆形
    mPaint.setColor(Color.BLUE);
    canvas.translate(200,200);
    canvas.drawCircle(0,0,100,mPaint);
    

    如果在缩放时稍微注意一下就会发现缩放的中心默认为坐标原点,而缩放中心轴就是坐标轴,如下:

    // 将坐标系原点移动到画布正中心
    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);
    

    (为了更加直观,我添加了一个坐标系,可以比较明显的看出,缩放中心就是坐标原点)



    下面对其中的一些概念和方法进行分析:

    状态栈:

    其实这个栈我也不知道叫什么名字,暂时叫做状态栈吧,它看起来像下面这样:



    这个栈可以存储画布状态和图层状态。
    Q:什么是画布和图层?
    A:实际上我们看到的画布是由多个图层构成的,如下图(图片来自网络):



    实际上我们之前讲解的绘制操作和画布操作都是在默认图层上进行的。
    在通常情况下,使用默认图层就可满足需求,但是如果需要绘制比较复杂的内容,如地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)等,则分图层绘制比较好一些。

    你可以把这些图层看做是一层一层的玻璃板,你在每层的玻璃板上绘制内容,然后把这些玻璃板叠在一起看就是最终效果。

    SaveFlags


    save

    save 有两种方法:

    // 保存全部状态
    public int save ()
    
    // 根据saveFlags参数保存一部分状态
    public int save (int saveFlags)
    

    可以看到第二种方法比第一种多了一个saveFlags参数,使用这个参数可以只保存一部分状态,更加灵活,这个saveFlags参数具体可参考上面表格中的内容。

    每调用一次save方法,都会在栈顶添加一条状态信息,以上面状态栈图片为例,再调用一次save则会在第5次上面再添加一条状态。

    saveLayerXxx

    saveLayerXxx有比较多的方法:

    // 无图层alpha(不透明度)通道
    public int saveLayer (RectF bounds, Paint paint)
    public int saveLayer (RectF bounds, Paint paint, int saveFlags)
    public int saveLayer (float left, float top, float right, float bottom, Paint paint)
    public int saveLayer (float left, float top, float right, float bottom, Paint paint, int saveFlags)
    
    // 有图层alpha(不透明度)通道
    public int saveLayerAlpha (RectF bounds, int alpha)
    public int saveLayerAlpha (RectF bounds, int alpha, int saveFlags)
    public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha)
    public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha, int saveFlags)
    

    注意:saveLayerXxx方法会让你花费更多的时间去渲染图像(图层多了相互之间叠加会导致计算量成倍增长),使用前请谨慎,如果可能,尽量避免使用。
    使用saveLayerXxx方法,也会将图层状态也放入状态栈中,同样使用restore方法进行恢复。
    这个暂时不过多讲述,如果以后用到详细讲解。(因为这里面东西也有不少啊QAQ)

    restore

    状态回滚,就是从栈顶取出一个状态然后根据内容进行恢复。
    同样以上面状态栈图片为例,调用一次restore方法则将状态栈中第5次取出,根据里面保存的状态进行状态恢复。

    restoreToCount

    弹出指定位置以及以上所有状态,并根据指定位置状态进行恢复。
    以上面状态栈图片为例,如果调用restoreToCount(2) 则会弹出 2 3 4 5 的状态,并根据第2次保存的状态进行恢复。

    getSaveCount

    获取保存的次数,即状态栈中保存状态的数量,以上面状态栈图片为例,使用该函数的返回值为5。
    不过请注意,该函数的最小返回值为1,即使弹出了所有的状态,返回值依旧为1,代表默认状态。

    常用格式

    虽然关于状态的保存和回滚啰嗦了不少,不过大多数情况下只需要记住下面的步骤就可以了:

    save();      //保存状态
    ...          //具体操作
    restore();   //回滚到之前的状态
    

    这种方式也是最简单和最容易理解的使用方法。
    文章来自 GcsSloop

    相关文章

      网友评论

        本文标题:自定义view进阶篇《三》——Canvas之画布操作

        本文链接:https://www.haomeiwen.com/subject/btvhattx.html