Android Canvas Layer 图层

Canvas 虽然一直被称之为 画布,当我们千万不要认为它就是现实生活中的一张画布,而是应该理解为 Photoshop 中的图层集合

更有趣的是 Android 中的 Canvas 具有记忆功能,会记住上次绘画时的坐标

Canvas 绘图机制

我们先来看一张图

drawCircle(50, 50, 50, mPaint) 参考坐标一直是 (50,50) 那为何会出现这样的效果?

那是因为,画布坐标原点每次分别在x,y轴上移动 100

那么假如我们要重新回到 (0,0) 点处绘制新的图形呢?translate(-100,-100) 的慢慢地平移回去?

肯定不是,我们可以在做平移变换之前将当前 canvas 的状态进行保存

Canvas 为我们提供了 图层(Layer) 的支持,Layer(图层)是按 "栈结构" 来进行管理的

当我们调用 save() 方法,会保存当前 Canvas 的状态然后作为一个 Layer(图层),添加到 Canvas栈 中,

而当我们调用 restore() 方法的时候,会恢复之前 Canvas 的状态,而此时 Canvas 的图层栈 会弹出栈顶的那个 Layer,后继的 Layer 来到栈顶,此时的 Canvas 回复到此栈顶时保存的 Canvas 状态

简单说就是 save()往栈压入一个 Layer,restore()弹出栈顶的一个Layer,这个Layer代表Canvas的 状态! 也就是说可以 save() 多次,也可以 restore() 多次,但是 restore() 的调用次数 不能大于** save()否则会引发错误

范例

我们写一个范例来验证下 save() 和 restore() 的作用

  1. 创建一个 空的 Android 项目 cn.twle.android.CanvasLayer

  2. 自定义一个 View 类 MsView.java

    package cn.twle.android.canvaslayer;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.support.v7.widget.AppCompatImageView;
    
    public class MsView extends AppCompatImageView {
    
        public MsView(Context context) {
    
            super(context, null);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            super.onDraw(canvas);
    
            Paint mPaint = new Paint();
    
            mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
            mPaint.setAntiAlias(true);// 抗锯尺
            mPaint.setColor(0xffff0000);
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    
            canvas.save();  //保存当前 canvas 的状态
    
            canvas.translate(100, 100);
            canvas.drawRect(200,200,200+300,200+200,mPaint);
    
            canvas.restore();  //恢复保存的Canvas的状态
            mPaint.setColor(0xff00ff00);
            canvas.drawRect(100,100,100+300,100+200,mPaint);
    
            invalidate();
        }
    }
    
  3. 修改 MainActivity.java 设置 setContentView(new MsView(MainActivity.this))

    package cn.twle.android.canvaslayer;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(new MsView(MainActivity.this));
        }
    }
    

运行结果

代码和结果已经说明了一切,接着我们搞得复杂点,来一发多个 save()restore()

修改 MsViewonDraw() 方法

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.save();

    Paint mPaint = new Paint();
    mPaint.setColor(0xffff0000);
    mPaint.setTextSize(48);

    canvas.translate(300, 300);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.translate(0, 200);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);

    invalidate();
}

运行结果

首先平移 (300,300) 画文字,然后旋转 45 度画文字,再接着旋转 45 度画文字,接着平移 (0,200)

期间每次画图前都 save() 一下,看到这里可能有个疑问,最后这个平移不是 y 移动 200么,怎么变成向左了 ?

这是因为,rotate() 是旋转的是整个坐标轴么

坐标轴的变化如下图

rotate() 应该弄懂了,现在我们看看 restore()

我们在最后绘图的前面加两个 restore()

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.save();

    Paint mPaint = new Paint();
    mPaint.setColor(0xffff0000);
    mPaint.setTextSize(48);

    canvas.translate(300, 300);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.restore();
    canvas.restore();

    canvas.translate(0, 200);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);

    invalidate();
}

运行结果

不说什么,自己体会,再加多个 restore()

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.save();

    Paint mPaint = new Paint();
    mPaint.setColor(0xffff0000);
    mPaint.setTextSize(48);

    canvas.translate(300, 300);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.restore();
    canvas.restore();
    canvas.restore();

    canvas.translate(0, 200);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);

    invalidate();
}

有点意思,继续加 restore()

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.save();

    Paint mPaint = new Paint();
    mPaint.setColor(0xffff0000);
    mPaint.setTextSize(48);

    canvas.translate(300, 300);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.rotate(45);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);
    canvas.save();

    canvas.restore();
    canvas.restore();
    canvas.restore();
    canvas.restore();

    canvas.translate(0, 200);
    canvas.drawText("简单教程,简单编程",0,0,mPaint);

    invalidate();
}

好像不可以再写 restore() 了是吧,因为我们只 save() 了四次

按照网上的说法,这会报错的,真的是这样吗?

我们可以调用 Canvas 给我们提供的一个获得当前栈中有多少个 Layer 的方法 getSaveCount() ;然后在 save()restore() 的前后都加一个 Log 将栈中 Layer 的层数打印出来

结果真是喜闻乐见,毕竟实践出真知

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.