Canvas详解

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/lylodyf/article/details/78853238

根据文档说明我们知道想要draw something有四个基本的要素:
- 一个保存像素的Bitmap
- 一个Canvas进行Bitmap的绘制
- 绘制的东西
- 画笔Paint

获取Canvas

获取Canvas实例我们一般都使用两种方式,一种是重写View的onDraw方法获得Canvas,另一种就是自己创建一个Canvas对象,创建Canvas对象我们需要一个Bitmap对象

 Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        //或者
        Canvas canvas = new Canvas();
        canvas.setBitmap(bitmap);

canvas的方法

填充

  • drawRGB(int r, int g, int b)
  • drawARGB(int a, int r, int g, int b)
  • drawColor(int color)
  • drawColor(int color, PorterDuff.Mode mode)
  • drawPaint(Paint paint)
    这几个方法都是用颜色去填充Bitmap,但都指出受限制于当前的clip,这个我们稍后再说

几何图形

画点

  • drawPoints(float[] pts, int offset, int count,Paint paint)
  • drawPoints(loat[] pts, Paint paint)
    drawPoint(float x, float y, Paint paint)

画线

  • drawLine(float startX, float startY, float stopX, float stopY,Paint paint)
  • drawLines(float[] pts, int offset, int count, Paint paint)
  • drawLines(float[] pts, Paint paint)

画矩形

  • drawRect(RectF rect, Paint paint)
  • drawRect(Rect r, Paint paint)
  • drawRect(float left, float top, float right, float bottom, Paint paint)

画椭圆

  • drawOval(RectF oval, Paint paint)
  • drawOval(float left, float top, float right, float bottom, Paint paint)

画弧

  • drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
  • drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, Paint paint)

画圆角矩形

  • drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint)
  • drawRoundRect(RectF rect, float rx, float ry, Paint paint)

画路径

  • drawPath(Path path, Paint paint)
  • drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
  • drawTextOnPath(String text, Path path, float hOffset,float vOffset,Paint paint)

画Bitmap

  • drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
  • drawBitmap(Bitmap bitmap, Rect src, RectF dst,Paint paint)
  • drawBitmap(Bitmap bitmap,Rect src, Rect dst,Paint paint)
  • drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

效果图

常用的大致就是上面的方法,看代码具体的运用
效果图
image

package com.lzy.lzy_canvas;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by lzy on 2016/10/20.
 */
public class CustomView extends View {
    private Paint mPaint;

    private float[] pts = {300.0f, 60.0f, 320.0f, 60.0f, 340.0f, 60.0f};

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Paint paintBlack = new Paint();
        paintBlack.setColor(Color.BLACK);
        Paint paintBlue = new Paint();
        paintBlue.setColor(Color.parseColor("#29C2D3"));

        Paint paint1 = new Paint();
        paint1.setColor(Color.parseColor("#68A11F"));

        Paint paint2 = new Paint();
        paint2.setColor(Color.parseColor("#5A1B8C"));
        paint2.setStyle(Paint.Style.STROKE);
        paint2.setStrokeWidth(5);

        //填充背景
        canvas.drawColor(Color.WHITE);

        //画点
        mPaint.setTextSize(60);
        canvas.drawText("画点", 50.0f, 100.0f, mPaint);
        canvas.drawPoints(pts, mPaint);

        //画线
        canvas.drawText("画线", 50.0f, 200.0f, mPaint);
        canvas.drawLine(100.0f, 300.0f, 800.0f, 300.0f, paintBlack);
        canvas.drawLines(new float[]{100.0f, 300.0f, 400.0f, 400.0f, 400.0f, 400.0f, 800.0f, 300.0f}, paintBlack);

        //画矩形
        canvas.drawRect(new Rect(0, 400, 1000, 600), mPaint);
//        canvas.drawRect(0, 400, 1000,600,mPaint);

        //画椭圆
        canvas.drawOval(new RectF(0f, 400f, 1000f, 600f), paintBlack);

        //画弧
        canvas.drawArc(new RectF(300f, 100f, 400f, 200f), 0f, 270f, true, paintBlue);
        canvas.drawArc(new RectF(500f, 100f, 700f, 200f), 0f, 120f, false, paintBlue);

        //圆角矩形
        canvas.drawRoundRect(new RectF(100f, 700f, 300f, 900f), 20f, 20f, paint1);

        //Path
        Path path = new Path();
        path.moveTo(400f, 700f);
        path.lineTo(600f, 700f);
        path.lineTo(700f, 800f);
        path.close();
        canvas.drawPath(path, paint1);
        //贝塞尔曲线
        Path path1 = new Path();
        path1.moveTo(800f, 700f);
        path1.quadTo(1000f, 700f, 1200f, 1500f);
        canvas.drawPath(path1, paint2);


        Path path2 = new Path();
        paint2.setTextSize(30);
        path2.moveTo(500f, 1200f);
        path2.quadTo(500f, 2000f, 200f, 1300f);
        canvas.drawPath(path2, paint2);
        paint2.setStyle(Paint.Style.FILL);
        String text = "加快了解罚款链接了框架安抚";
        //第三个参数是距离开始点的距离,第四个参数是距离线的距离
        canvas.drawTextOnPath(text, path2, 50, 0, paint2);

        //Bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        //其中两个参数是决定位置的
        canvas.drawBitmap(bitmap, 0, 1200, new Paint());
        //第二个参数相当于裁剪,意思是要截取Bitmap哪一部分来显示
        //第三个参数是放置位置,放在canvas的哪个位置
        canvas.drawBitmap(bitmap, new Rect(40, 40, 100, 100), new Rect(200, 1600, 260, 1660), null);


    }
}

Translate

canvas.translate()画布的平移操作,画布的顶点默认位置是在(0,0),当调用canvas.translate(100,100)把画布顶点平移到(100,100)

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.drawRect(new Rect(50, 50, 250, 250), mPaint);
        canvas.translate(100, 100);
        //顶点在(100,100)
        canvas.drawRect(new Rect(50, 50, 250, 250), mPaint);
        canvas.translate(100, 100);
        //顶点在(200,200)
        canvas.drawRect(new Rect(50, 50, 250, 250), mPaint);

        Rect frame = new Rect(50, 300, 300, 310);
        for (int i = 0; i < 20; i++) {
            canvas.drawRect(frame,mPaint);
            canvas.translate(0,20);
        }
    }

image

Scale

  • scale(float sx, float sy)
  • scale(float sx, float sy, float px, float py)
    有两个方法,先看效果
 @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.RED);
        canvas.drawRect(new Rect(50, 50, 250, 250), mPaint);
        canvas.scale(0.5f,0.5f,150f,150f);
//        canvas.scale(0.5f, 0.5f);
        mPaint.setColor(Color.CYAN);
        canvas.drawRect(new Rect(50, 50, 250, 250), mPaint);

    }

第一张是使用canvas.scale(0.5f, 0.5f)的结果,我们发现四个参数都减少了一半。
第二张是使用canvas.scale(0.5f,0.5f,150f,150f)的结果,明显不一样,看源码


public final void scale(float sx, float sy, float px, float py) {
        translate(px, py);
        scale(sx, sy);
        translate(-px, -py);
    }

可以发现把顶点先移动到指定位置,然后缩放,最后再平移到原来的位置,意思是后面指定的点就是缩放的顶点,不变。
利用scale可以简单实现下面的效果


@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        for (int i = 0; i < 10; i++) {
            canvas.drawRect(new Rect(50, 50, 550, 550), mPaint);
            canvas.scale(0.8f, 0.8f, 300, 300);
        }

    }

Rotate

  • rotate(float degrees)
  • rotate(float degrees, float px, float py)
    和Scale类似,也是两个方法,第二方法传入的坐标点也和scale一样,相当于基准点

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.RED);
        canvas.drawRect(new Rect(50, 50, 350, 350), mPaint);
        //原点旋转
//        canvas.rotate(30);
        //绕中心点旋转
        canvas.rotate(30,200,200);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(new Rect(50, 50, 350, 350), mPaint);

    }

分别是绕原点和中心点的效果

Skew

  • skew(float sx, float sy)
  • sx,sy分别是x,y方向的倾斜tan值
    y方向倾斜45度
    这个变化后的值是怎么计算的?
    比如右上角的点为(50,50)
    由于x方向为0,只进行y方向的变化,变化后的y为
    y+sy*x,sy就是传入的第二个参数,即50+1*350=400,所以变化后坐标为(350,400)
@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.RED);
        canvas.drawRect(new Rect(50, 50, 350, 350), mPaint);

        canvas.skew(0, 1);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(new Rect(50, 50, 350, 350), mPaint);

    }

save()和restore()

这是用于Canvas的状态保存和回滚,save()可以保存当前的matrix和clip操作到一个私有的栈中,restore()可以回滚到之前的保存状态,比如下面的代码,就是先保存当前的状态,然后把画布平移到了(10,10)这里,但是当执行了restore()之后,画布又回到了之前的状态,也就是(0,0)

canvas.save();
        canvas.translate(10, 10);
        drawSample(canvas);
        canvas.restore();

裁剪

主要是clipRect(),clipPath()方法,前者显然是裁剪矩形,后者是裁剪路径
裁剪即裁剪画布,只有裁剪的区域才能显示出绘制的内容。试验一下

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.clipRect(new Rect(100,100, 200, 200));
        canvas.drawColor(Color.RED);

    }

效果如下,可以看到红色只是在裁剪的区域显示出来

当我们调用方法时可能发现有的方法后面需要传入一个Region.Op参数,那么这个Region.Op又是什么东西?容我喝口水慢慢道来~
它表示当前裁剪区域和之前裁剪区域的关系,意思是需要至少裁剪两次这个参数才会起作用,对吧。这个参数的值是枚举类型,具体看看有哪些取值
- DIFFERENCE:之前剪切过除去当前要剪切的区域
- INTERSECT:当前要剪切的区域在之前剪切过内部的部分
- UNION:当前要剪切的区域加上之前剪切过内部的部分
- XOR:异或,当前要剪切的区域与之前剪切过的进行异或
- REVERSE_DIFFERENCE:与DIFFERENCE相反,以当前要剪切的区域为参照物,当前要剪切的区域除去之前剪切过的区域
- REPLACE:用当前要剪切的区域代替之前剪切过的区域
其实当我们不传入这个参数时,它会默认给INTERSECT

是不是很清楚啦?没有?那看看下面的具体例子吧

package com.lzy.lzy_canvas;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by lzy on 2016/10/20.
 */
public class CustomView2 extends View {
    private Paint mPaint;


    public CustomView2(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(6);
        mPaint.setTextSize(16);
        mPaint.setTextAlign(Paint.Align.RIGHT);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        canvas.translate(10, 10);
        drawSample(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(560, 10);
        //剪切掉外面一层
        canvas.clipRect(50, 50, 450, 450);
        //DIFFERENCE:之前剪切过除去当前要剪切的区域.
        // 去掉中间剪切的区域,所以只剩一个环
        canvas.clipRect(150, 150, 350, 350, Region.Op.DIFFERENCE);
        drawSample(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(10, 560);
        canvas.clipRect(150, 150, 350, 350);
        //REPLACE:当前剪切的区域覆盖之前剪切的区域
        canvas.clipRect(50, 50, 450, 450, Region.Op.REPLACE);
        drawSample(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(560, 560);
        canvas.clipRect(0, 0, 160, 160);
        //UNION:当前的剪切区域加上之前剪切的区域
        canvas.clipRect(100, 100, 200, 200, Region.Op.UNION);
        drawSample(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(10, 1110);
        canvas.clipRect(0, 0, 160, 160);
        //XOR:当前截取区域和之前剪切区域进行异或,就是UNION去掉重叠的部分
        canvas.clipRect(100, 100, 200, 200, Region.Op.XOR);
        drawSample(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(560, 1110);
        canvas.clipRect(0, 0, 160, 160);
        //REVERSE_DIFFERENCE:和DIFFERENCE相反,当前要剪切区域去掉之前剪切的区域
        canvas.clipRect(100, 100, 200, 200, Region.Op.REVERSE_DIFFERENCE);
        drawSample(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(10, 1620);
        canvas.clipRect(0, 0, 160, 160);
        //INTERSECT:当前裁剪区域在之前裁剪区域的内部部分,就相当于交集
        canvas.clipRect(100, 100, 200, 200, Region.Op.INTERSECT);
        drawSample(canvas);
        canvas.restore();

    }

    private void drawSample(Canvas canvas) {
        canvas.clipRect(0, 0, 500, 500);

        canvas.drawColor(Color.WHITE);

        mPaint.setColor(Color.RED);
        canvas.drawCircle(250, 250, 250, mPaint);

    }
}

效果如下,可以对照着代码看,容易理解,左上角是没有经过剪切的样子

猜你喜欢

转载自blog.csdn.net/lylodyf/article/details/78853238