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

开篇

前面很多篇文章都用到了图层的概念,但是一直没有详细介绍,今天这篇文章将详细的介绍 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
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
其实前端动画和 Canvas 是可以结合起来使用的,而且 Canvas 绘制透明的图片也很简单。以下是示例代码: ```html <!DOCTYPE html> <html> <head> <title>Canvas 绘制透明的图片</title> </head> <body> <canvas id="myCanvas"></canvas> <script> const canvas = document.getElementById('myCanvas'); const context = canvas.getContext('2d'); // 创建一个 Image 对象 const img = new Image(); // 设置图片源 img.src = 'https://example.com/transparent-image.png'; // 在图片加载完成后绘制图片 img.onload = function() { // 将画布设置为图片大小 canvas.width = img.width; canvas.height = img.height; // 绘制图片 context.drawImage(img, 0, 0); }; </script> </body> </html> ``` 以上代码中,我们先创建了一个 Canvas 元素,并获取了它的上下文对象。接着创建了一个 Image 对象并设置了图片源。在图片加载完成后,我们将画布的尺寸设置为图片的尺寸,并使用 `drawImage` 方法将图片绘制到画布上。 如果需要实现动画效果,可以使用 `requestAnimationFrame` 方法来更新 Canvas 上的图像。例如: ```html <!DOCTYPE html> <html> <head> <title>Canvas 绘制透明的图片</title> </head> <body> <canvas id="myCanvas"></canvas> <script> const canvas = document.getElementById('myCanvas'); const context = canvas.getContext('2d'); // 创建一个 Image 对象 const img = new Image(); img.src = 'https://example.com/transparent-image.png'; // 定义图片的位置和速度 let x = 0; let y = 0; let vx = 5; let vy = 5; // 定义更新画面的函数 function update() { // 擦除画布 context.clearRect(0, 0, canvas.width, canvas.height); // 绘制图片 context.drawImage(img, x, y); // 更新图片位置 x += vx; y += vy; // 检查是否撞到画布边缘 if (x < 0 || x + img.width > canvas.width) { vx = -vx; } if (y < 0 || y + img.height > canvas.height) { vy = -vy; } // 在下一帧更新画面 requestAnimationFrame(update); } // 在图片加载完成后开始动画 img.onload = function() { canvas.width = img.width; canvas.height = img.height; update(); }; </script> </body> </html> ``` 以上代码中,我们定义了 `update` 函数来更新 Canvas 上的图像。在每一帧更新时,我们先擦除画布,然后绘制图片,并根据速度更新图片的位置。最后检查图片是否撞到画布边缘,如果是则反转速度。在下一帧更新时再次调用 `update` 函数。在图片加载完成后,我们将画布的尺寸设置为图片的尺寸,并开始动画。 希望以上代码可以帮助你理解如何在 Canvas 上绘制透明的图片。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值