Android高级进阶——绘图篇(七)Canvas 与 图层(一)

本文深入探讨Android中的Canvas与图层的概念,包括如何获取Canvas对象、图层与画布的关系、平移、旋转、缩放、扭曲、裁剪以及画布的保存与恢复。特别讨论了saveLayer()、saveLayerAlpha()的用法和它们在绘图流程中的影响,强调了画布、图层与屏幕显示的区别,并提供了实例分析。
摘要由CSDN通过智能技术生成

开篇

前面很多篇文章都用到了图层的概念,但是一直没有详细介绍,今天这篇文章将详细的介绍 Canvas 与 图层的概念

一、如何获得一个Canvas对象

  • 方法一:自定义view时, 重写onDraw、dispatchDraw方法
protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  
}  

protected void dispatchDraw(Canvas canvas) {  
    super.dispatchDraw(canvas);  
}  
  • 方法二:使用Bitmap创建
    使用:

    Canvas c = new Canvas(bitmap);


Canvas c = new Canvas();
c.setBitmap(bitmap);

二、在OnDraw()中使用

我们一定要注意的是,如果我们用bitmap构造了一个canvas,那这个canvas上绘制的图像也都会保存在这个bitmap上,而不是画在View上,如果想画在View上就必须使用OnDraw(Canvas canvas)函数中传进来的canvas画一遍bitmap才能画到view上。 下面举个例子: ![image.png](https://upload-images.jianshu.io/upload_images/11455341-de84946824e77d53.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    private void init() {
        //初始化画笔
        paint = new Paint();
        //设置画笔颜色
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setTextSize(50);

        bitmap = Bitmap.createBitmap(800, 400, Bitmap.Config.ARGB_8888);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //禁用硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        Canvas canvas1 = new Canvas(bitmap);
        canvas1.drawText("你是来搞笑的么!!!", 100, 100, paint);
        canvas.drawBitmap(bitmap, 100, 100, paint);
    }
可以看到,毛线也没有,这是为什么呢? 我们仔细来看一下onDraw函数:
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //禁用硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        Canvas canvas1 = new Canvas(bitmap);
        canvas1.drawText("你是来搞笑的么!!!", 100, 100, paint);

    }
在onDraw函数中,我们只是将文字画在了mBmpCanvas上,也就是我们新建mBmp图片上!这个图片跟我们view没有任何关系好吧,我们需要把mBmp图片画到view上才行,所以我们在onDraw中需要加下面这句,将mBmp画到view上
canvas.drawBitmap(bitmap, 100, 100, paint);
![](https://upload-images.jianshu.io/upload_images/11455341-c918f89cd0013070.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

三、图层与画布

  • (1)、画布的平移(translate)
    canvas中有一个函数translate()是用来实现画布平移的,画布的原状是以左上角为原点,向左是X轴正方向,向下是Y轴正方向

void translate(float dx, float dy)

参数说明:
- float dx:水平方向平移的距离,正数指向正方向(向右)平移的量,负数为向负方向(向左)平移的量
- flaot dy:垂直方向平移的距离,正数指向正方向(向下)平移的量,负数为向负方向(向上)平移的量

protected void onDraw(Canvas canvas) {  
    // TODO Auto-generated method stub  
    super.onDraw(canvas);  

    //translate  平移,即改变坐标系原点位置  

    Paint paint = new Paint();  
    paint.setColor(Color.GREEN);  
    paint.setStyle(Style.FILL);  

//  canvas.translate(100, 100);  
    Rect rect1 = new Rect(0,0,400,220);  
    canvas.drawRect(rect1, paint);  

1、上面这段代码,先把canvas.translate(100, 100);注释掉,看原来矩形的位置,然后打开注释,看平移后的位置,对比如下图:

image.png

二、屏幕显示与Canvas的关系

很多童鞋一直以为显示所画东西的改屏幕就是Canvas,其实这是一个非常错误的理解,比如下面我们这段代码:

这段代码中,同一个矩形,在画布平移前画一次,平移后再画一次,大家会觉得结果会怎样?

protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);

//构造两个画笔,一个红色,一个绿色  
Paint paint_green = generatePaint(Color.GREEN, Style.STROKE, 3);  
Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 3);  

//构造一个矩形  
Rect rect1 = new Rect(0,0,400,220);  

//在平移画布前用绿色画下边框  
canvas.drawRect(rect1, paint_green);  

//平移画布后,再用红色边框重新画下这个矩形  
canvas.translate(100, 100);  
canvas.drawRect(rect1, paint_red);  

}
private Paint generatePaint(int color,Paint.Style style,int width)
{
Paint paint = new Paint();
paint.setColor(color);
paint.setStyle(style);
paint.setStrokeWidth(width);
return paint;
}

代码分析:
这段代码中,对于同一个矩形,在平移画布前利用绿色画下矩形边框,在平移后,再用红色画下矩形边框。大家是不是会觉得这两个边框会重合?实际结果是这样的。

image.png

从到这个结果大家可能会狠蛋疼,我第一次看到这个结果的时候蛋都碎一地了要。淡定……
这个结果的关键问题在于,为什么绿色框并没有移动?

这是由于屏幕显示与Canvas根本不是一个概念!Canvas是一个很虚幻的概念,相当于一个透明图层(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示。所以上面的两个结果是由下面几个步骤形成的:

1、调用canvas.drawRect(rect1, paint_green);时,产生一个Canvas透明图层,由于当时还没有对坐标系平移,所以坐标原点是(0,0);再在系统在Canvas上画好之后,覆盖到屏幕上显示出来,过程如下图:

image.png

2、然后再第二次调用canvas.drawRect(rect1, paint_red);时,又会重新产生一个全新的Canvas画布,但此时画布坐标已经改变了,即向右和向下分别移动了100像素,所以此时的绘图方式为:(合成视图,从上往下看的合成方式)

image.png

上图展示了,上层的Canvas图层与底部的屏幕的合成过程,由于Canvas画布已经平移了100像素,所以在画图时是以新原点来产生视图的,然后合成到屏幕上,这就是我们上面最终看到的结果了。我们看到屏幕移动之后,有一部分超出了屏幕的范围,那超出范围的图像显不显示呢,当然不显示了!也就是说,Canvas上虽然能画上,但超出了屏幕的范围,是不会显示的。当然,我们这里也没有超出显示范围,两框框而已。

下面对上面的知识做一下总结:
- 1、每次调用canvas.drawXXXX系列函数来绘图进,都会产生一个全新的Canvas画布。
- 2、如果在DrawXXX前,调用平移、旋转等函数来对Canvas进行了操作,那么这个操作是不可逆的!每次产生的画布的最新位置都是这些操作后的位置。(关于Save()、Restore()的画布可逆问题的后面再讲)
- 3、在Canvas与屏幕合成时,超出屏幕范围的图像是不会显示出来的。

四、旋转(Rotate)

画布的旋转是默认是围绕坐标原点来旋转的,这里容易产生错觉,看起来觉得是图片旋转了,其实我们旋转的是画布,以后在此画布上画的东西显示出来的时候全部看起来都是旋转的。其实Roate函数有两个构造函数:

void rotate(float degrees)
void rotate (float degrees, float px, float py)

第一个构造函数直接输入旋转的度数,正数是顺时针旋转,负数指逆时针旋转,它

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值