Android绘图篇(一)——Canvans基本操作

前言:Canvas,是Android绘图机制的核心api,可以绘制出矩形、圆形、贝塞尔曲线、路径、文字等等各种图形,在Android的自定义View中需要大量用到这个类。现实生活中,我们在纸上画出一幅画,需要三样东西:画笔、画板、纸张。而Canvas就相当于画板,至于为什么不是纸张,咱们后面再说~

Paint

Paint,画笔,绘图三要素之一。常用api如下:

        //初始化画笔
        paint = new Paint();
        //设置抗锯齿
        paint.setAntiAlias(true);
        //设置画笔宽度
        paint.setStrokeWidth(5);
        //设置画笔颜色
        paint.setColor(Color.RED);
         //设置画笔透明度
        paint.setAlpha(128);
        //设置画笔样式
        paint.setStyle(Paint.Style.STROKE);

经过这样设置的Paint对象,已经可以用来绘制各种各样的图形了,当然还有许多其它的api,不是很常用,这里就不做介绍。

Canvans来源

画板,我们在自定义View的时候,通常会重写其中的onDraw()方法:

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

这个方法默认提供了一个Canvas对象,通常我们会在自定义View的构造方法中去初始化Paint对象,然后配合提供的Canvas对象,就可以在自定义View的时候绘去制各种各样的图形。然而,这是重写View的方法时候默认提供的,如果我们想自己去初始化Canvans对象该怎么弄呢?也很简单:

  Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);

为啥要传入一个Bitmap对象呢?刚我们说现实生活中画一幅画的三要素是画板、画笔、纸张,画笔和画板都有了,而Bitmap就是最后一个要素:纸张。用以存储Canvas绘制的各种图形。Canvas有空参数的构造方法:

 /**
     * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to
     * draw into.  The initial target density is {@link Bitmap#DENSITY_NONE};
     * this will typically be replaced when a target bitmap is set for the
     * canvas.
     */
    public Canvas() {
        if (!isHardwareAccelerated()) {
            // 0 means no native bitmap
            mNativeCanvasWrapper = nInitRaster(null);
            mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
                    this, mNativeCanvasWrapper);
        } else {
            mFinalizer = null;
        }
    }

重点不看代码,看它的注释,Construct an empty raster canvas. Use setBitmap() to specify a bitmap to draw into。翻译过来就是:构造一个空的Canvas,使用setBitmap()方法指定一个Bitmap对象用于画入内容。意思就是,即便你使用空参数的构造方法,也需要调用setBitmap方法为其指定一个Bitmap对象,用于存储画出来的内容。以后你用Canvas的api画出来的各种图形,都是作用在你指定的Bitmap对象上的。 可以指定任意的Bitmap。好吧,这里也顺带说一下 Bitmap.Config这个类,createBitmap时传入的第三个参数,它是一个枚举类,用于决定图片存储的质量和大小。

每个像素信息只存储了alpha这一项信息。
public enum Config {
        //每个像素只存储透明度信息
        ALPHA_8

        //存储R、G、B信息三原色的信息。Red 5位;Green 6位;Blue 5位,5+6+5=16位=2个字节。
        RGB_565

        //存储透明度和三原色的信息。Alpha 4位;Red 4位;Green 4位;Blue 4位,4+4+4+4=16位=2个字节
        ARGB_4444

        //存储透明度和三原色的信息。Alpha 8位;Red 8位;Green 8位;Blue 8位,8+8+8+8=32位=4个字节
        ARGB_8888
        
        //图片以8个字节存储
        RGBA_F16
    }   

其实就是指定了每个像素的A、R、G、B所占的位数,合起来就是一个像素点所占的大小,可以认为每个像素点所占的大小越大,图片的质量越高,官方推荐使用ARGB_8888。举个例子,一张图片的分辨率是19201080,采用的是ARGB_8888格式,则它在内存中占用的大小是19201080*4/1000/1000=8.29M,这个值已经很大了,Android为每个应用分配的内存默认是16M,不同的厂商可能会修改这个值,但是也架不住这样的内存使用率。这就又涉及到图片的内存优化了,本篇博客重点不在这里,感兴趣的小伙伴可以自行百度Android图片内存优化相关的文章。好了,我们要开始画画了~

1、使用Canvas画圆

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //设置画笔
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
        paint.setAntiAlias(true);

        //画圆 
        canvas.drawCircle(getWidth()/2, getHeight()/2, 100, paint);

    }

第一个参数是圆心坐标点的x值,第二个参数是圆心左边的y值,第三个参数是画笔,坐标相对于View的左上角。

 //画矩形
 canvas.drawRect(100,100,300,300,paint);

前两个参数是矩形左上角的坐标,后两个参数是矩形右下角的坐标,最后一个是画笔对象。除了这种方法,还能直接传入一个Rect或者RectF对象:

 //画矩形
 //RectF rect = new RectF(100,100,300,300);
 Rect rect = new Rect(100,100,300,300);
 canvas.drawRect(rect,paint);

效果是一样的,Rect和RectF都表示一个矩形的区域,功能都一样,不同的是Rect中的参数是整型的,RectF中的参数是单精度浮点型的。效果如下:


Android绘图篇(一)——Canvans基本操作_第1张图片

3、使用Canvas画圆角矩形

    //画圆角矩形
    RectF rect = new RectF(100,100,300,300);
    canvas.drawRoundRect(rect,10,10,paint);

Rect和Paint对象自不用说,中间的两个参数是啥呢?分别是矩形外接椭圆的x轴和y轴的半径,这么说不好理解:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorAccent"/>
    <corners android:radius="5dp"></corners>
</shape>

类似于上面代码中的radius属性。效果嘛,如下:


Android绘图篇(一)——Canvans基本操作_第2张图片

当然还有个重载的方法,把RectF对象拆成两个坐标点:

canvas.drawRoundRect(100,100,300,300,10,10,paint);

效果是一样的。

4、使用Canvas绘制椭圆

//绘制椭圆
RectF rect = new RectF(100,100,400,300);
canvas.drawOval(rect,paint);

为何要传入一个矩形的对象,这涉及到高中的数学知识了,已知一个矩形,其内切椭圆的值是唯一的,所以可以根据矩形来绘制出唯一的内切椭圆。


Android绘图篇(一)——Canvans基本操作_第3张图片
好吧,画出来的椭圆如下:

Android绘图篇(一)——Canvans基本操作_第4张图片

注意:如果矩形是正方形的话,则绘制出来椭圆的是圆形。

5、使用Canvas绘制弧线

弧线来自于圆或者椭圆,而椭圆来自于矩形内切,所以绘制弧线依旧需要一个矩形作为参数:

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) 

每个参数的意义如下:

  1. oval:矩形对象,弧线来自这个矩形的内切椭圆。
  2. float:起始角度。
  3. sweepAngle:弧线扫过的角度。注意不是结束角度,是弧线扫过的角度,方向是顺时针。
  4. useCenter:是否显示弧边。
  5. paint:画笔。

注:角度的定义参考高中数学象限知识。
好吧,画一个试试呗:

 //绘制弧线
 RectF rect = new RectF(100,100,400,300);
 canvas.drawArc(rect,0,45,true,paint);

效果如下:


Android绘图篇(一)——Canvans基本操作_第5张图片

从0度开始,顺时针扫过45度,绘制出来的弧形(或者说扇形),如果把paint的style设置成Style.STROKE,图形还是一样的,只不过不填充了


Android绘图篇(一)——Canvans基本操作_第6张图片

如果不绘制弧边:

//绘制弧线,不显示弧边
RectF rect = new RectF(100, 100, 400, 300);
canvas.drawArc(rect, 0, 45, false, paint);

效果是这样的:


Android绘图篇(一)——Canvans基本操作_第7张图片

好吧,这个api既可以绘制扇形,也可以绘制一小段弧线,通过useCenter参数来控制即可。

6、使用Canvas绘制直线

public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
  1. startX:起点的x坐标。
  2. startY:起点的y坐标。
  3. stopX:终点的x坐标。
  4. stopY:终点的y坐标
  5. paint:画笔。

举例:

canvas.drawLine(30,30,100,100,paint);

效果:


Android绘图篇(一)——Canvans基本操作_第8张图片

绘制多条直线:

public void drawLines(float[] pts, Paint paint)
  1. pts:float数组,所有直线点的集合。比如:{100, 100, 200,200,300,300,500,500}则表示两条直线,(100,100)是第一条直线的起点,(200,200)是第一条直线的终点。(300,300)是第二条直线的起点,(500,500)是第二条直线的终点,以此内推。。。
  2. paint:画笔对象。

注:float数组中两个元素确定一个点,四个元素确定一条直线,元素个数可以是任意个数,少于四个元素则无法绘制任何直线,大于四个元素,则可以绘制 (int)(元素个数/4) 条直线。

举例:

 float[] pts = {100, 100, 200,200,300,300,500,500};
 canvas.drawLines(pts,paint);

效果


Android绘图篇(一)——Canvans基本操作_第9张图片

还有个重载的方法:

 public void drawLines(float[] pts, int offset, int count,Paint paint)
  1. pts:float数组。
  2. offset:跳过的数组元素个数,这些数据将不参与绘制过程。
  3. count:实际参与绘制的数组元素个数。
  4. paint:画笔对象

举个例子:

float[] pts = {
               100, 100, 200, 200,
               300, 300, 400, 400,
               500, 500, 600, 600
        };
canvas.drawLines(pts,4,4,paint);

这里有三条直线,跳过最前面四个元素,且实际参与绘制的数组元素个数为4,则数组中只有{300, 300, 400, 400}四个元素参与到绘制了,所以只会绘制中间一条直线。


Android绘图篇(一)——Canvans基本操作_第10张图片

7、使用Canvas填充控件颜色

假设一个自定义View的大小是100px*100px,如何去设置整个控件的颜色呢?当然,可以设置一个背景,也可以通过View自身的onDraw方法去绘制控件自身的颜色。

canvas.drawARGB(255,189,60,100);

四个值分别是A、R、G、B。


Android绘图篇(一)——Canvans基本操作_第11张图片

也可以使用drawColor()方法指定颜色:

  canvas.drawColor(Color.parseColor("#FFBD3C64"));

效果是一模一样的。

Canvas还有许多其它的api,如drawBitmap、drawPicture、drawText等等,这几个api涉及的东西比较多,这篇博客一时写不完,有时间会另写一篇博客介绍,文中如果有错误或者理解不到位的地方,欢迎小伙伴批评指正,完~

下一篇:Android绘图篇——绘制文本

你可能感兴趣的:(Android自定义控件)