Android Canvas画布解析

4,520 阅读12分钟

1.简介

在开发中,我们经常需要自定义View去实现各种各样的效果,在这个过程中经常需要用到Canvas画布去绘制各种各样的图形和图案,因此,熟练地掌握Canvas的各种使用方法,就显得尤为重要。本文将简要介绍Canvas的各种用法,加深大家的理解。

2.绘制各种图形

Canvas提供了很多绘制方法,基于这些方法,我们可以绘制出各种各样的图形,下面我们就开始介绍这些绘制方法。

2.1 drawARGB

此方法可以用ARGB颜色绘制一个颜色背景,方法如下:

//a:颜色的alpha部分,取值0--255
//r:颜色的red部分,取值0--255
//g:颜色的green部分,取值0--255
//b:颜色的blue部分,取值0--255
public void drawARGB(int a, int r, int g, int b)

现在使用此方法绘制一个纯色背景,示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawARGB(255,150,100,100);
}

2.2 drawArc

先介绍其中的一个方法,方法如下:

//left:左边到父布局左边的距离
//top:顶边到父布局顶边的距离
//right:右边到父布局左边的距离
//bottom:底边到父布局顶边的距离
//startAngle:弧开始的角度
//sweepAngle:顺时针方向扫描的角度
//useCenter:是否使用中心
//paint:绘制弧的画笔,这个值不能为null
public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)

此方法用来绘制弧形,如果起始角度是负值或大于等于360,起始角度取360的模。如果扫描角度大于等于360,椭圆形将会被完全地绘制,如果扫描角度是负值,扫描角度取360的模。弧的绘制是顺时针方向,0度对应着钟表的3点钟方向。useCenter为true时的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawArc(50,50,300,300,0,300,true,paint);
}

useCenter为false时的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawArc(50,50,300,300,0,300,false,paint);
}

drawArc的另一个重载方法如下:

//oval:用来定义弧形的形状和大小的椭圆的边界,这个值不能为null
//startAngle:弧开始的角度
//sweepAngle:顺时针方向扫描的角度
//useCenter:是否使用中心
//paint:绘制弧的画笔,这个值不能为null
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)

此方法useCenter为true时的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(50,50,300,300);
    canvas.drawArc(rectF,0,200,true,paint);
}

此方法useCenter为false时的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(50,50,300,300);
    canvas.drawArc(rectF,0,200,false,paint);
}

2.3 drawBitmap

这个方法是用来绘制位图的,这个方法有很多重载,先看其中的一个方法:

//bitmap:要绘制的位图
//matrix:用来变换位图的矩阵
//paint:绘制位图的画笔,可能为null
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)

此方法绘制位图的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
    Matrix matrix = new Matrix();
    canvas.drawBitmap(bitmap,matrix,paint);
}

看一个drawBitmap的重载方法如下:

//bitmap:要绘制的位图
//src:要绘制的位图的子集,即绘制的是全部或者部分位图,可能为null
//dst:位图将要通过缩放和转换去适应的矩形
//paint:绘制位图的画笔,可能为null
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
            @Nullable Paint paint)

此方法用来绘制位图,通过自动缩放和转换去适应目标矩形,示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
    Rect srcRect = new Rect(0,0,300,300);
    Rect dstRect = new Rect(0,0,600,600);
    canvas.drawBitmap(bitmap,srcRect,dstRect,paint);
}

再来看另外一个重载方法如下:

//bitmap:要绘制的位图
//src:要绘制的位图的子集,即绘制的是全部或者部分位图,可能为null
//dst:位图将要通过缩放和转换去适应的矩形
//paint:绘制位图的画笔,可能为null
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
            @Nullable Paint paint)

此方法也是用来绘制通过自动缩放和转换去适应目标矩形的位图,示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
    Rect srcRect = new Rect(0,0,300,300);
    RectF dstRectF = new RectF(0,0,600,600);
    canvas.drawBitmap(bitmap,srcRect,dstRectF,paint);
}

再来看绘制位图的一个方法如下:

//bitmap:要绘制的位图
//left:位图左边的位置
//top:位图顶边的位置
//paint:绘制位图的画笔,可能为null
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)

这个方法绘制左上角在(x,y)的位图,如果位图和画布拥有不同的密度,将会自动缩放位图,以和画布相同的密度绘制,示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
    canvas.drawBitmap(bitmap,50,100,paint);
}

2.4 drawCircle

此方法使用画笔paint绘制圆,如果半径小于等于0将不会绘制任何东西,基于画笔的样式,圆将会被填充或者绘制的是轮廓,方法如下:

//cx:圆心的x坐标
//cy:圆心的y坐标
//radius:圆的半径
//paint:绘制圆的画笔
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

此方法的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawCircle(200,200,150,paint);
}

2.5 drawColor

此方法使用颜色填充整个画布canvas的位图,方法如下:

//color:绘制在画布上的颜色
public void drawColor(@ColorInt int color)

示例如下:

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

再来看一个重载方法如下:

//color:绘制在画布上的颜色
//mode:应用到颜色上的porter-duff模式
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode)

此方法使用颜色和porter-duff模式填充整个画布canvas的位图,示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.GREEN, PorterDuff.Mode.DARKEN);
}

2.6 drawLine

此方法使用画笔paint和开始及终止的x,y坐标绘制线段,由于线总是轮廓式的,画笔paint的样式将会被忽略,方法如下:

//startX:线段起始点的x坐标
//startY:线段起始点的y坐标
//stopX:线段结束点的x坐标
//stopY:线段结束点的y坐标
//paint:绘制线段的画笔
public void drawLine(float startX, float startY, float stopX, float stopY,
            @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawLine(50,50,300,300,paint);
}

2.7 drawLines

此方法绘制一系列线段,每条线需要pts数组中4个连续的值。因此,绘制一条线,数组必须至少包括4个值。逻辑上和绘制下面的数组一样,先使用pts[0]、pts[1]、pts[2]、pts[3]绘制线,接着使用[4]、pts[5]、pts[6]、pts[7]绘制线,以此类推。方法如下:

//pts:要绘制的点的数组,如[x0,y0,x1,y1,x2,y2...]
//offset:绘制前在数组中要跳过的值的个数
//count:在跳过偏移量后,要处理的数组中值的个数
//paint:绘制的画笔
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
            @NonNull Paint paint)

此方法的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    float[] pts = {50,10,100,200,300,200,200,400};
    canvas.drawLines(pts,0,8,paint);
}

再来看另一个重载方法如下:

//pts:要绘制的点的数组,如[x0,y0,x1,y1,x2,y2...]
//paint:绘制的画笔
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    float[] pts = {50,10,100,200,300,200,200,400};
    canvas.drawLines(pts,paint);
}

2.8 drawOval

此方法使用画笔paint绘制椭圆,椭圆被填充或是轮廓由画笔paint的样式决定,方法如下:

//left:左边到父布局左边的距离
//top:顶边到父布局顶边的距离
//right:右边到父布局左边的距离
//bottom:底边到父布局顶边的距离
//paint:绘制的画笔
public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawOval(50,50,600,300,paint);
}

再来看一个重载方法:

//oval:椭圆的矩形边界,这个值不能为null
//paint:绘制的画笔,不能为null
public void drawOval(@NonNull RectF oval, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(50,50,600,300);
    canvas.drawOval(rectF,paint);
}

2.9 drawPaint

此方法使用画笔paint填充整个画布的位图,方法如下:

//paint:在画布上绘制的画笔
public void drawPaint(@NonNull Paint paint)

2.10 drawPath

此方法使用画笔paint绘制路径,路径被填充或是轮廓由画笔paint的样式决定,方法如下:

//path:被绘制的路径
//paint:绘制路径的画笔
public void drawPath(@NonNull Path path, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Path path = new Path();
    path.moveTo(50,50);
    path.lineTo(200,100);
    path.lineTo(200,400);
    path.lineTo(150,500);
    canvas.drawPath(path,paint);
}

2.11 drawPoint

此方法用来绘制一个点,方法如下:

//x:点的x坐标
//y:点的y坐标
//paint:绘制点的画笔
public void drawPoint(float x, float y, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPoint(100,100,paint);
}

2.12 drawPoints

此方法绘制一系列点,每个点位于被pts[]确定的坐标的中心,点的直径由画笔的笔画宽度确定,点的形状由画笔的Cap类型确定,点的形状是正方形的,除非当Cap类型是Round的时候,点的形状是圆形的,方法如下:

//pts:要绘制的点的数组[x0,y0,x1,y1,x2,y2...]
//offset:绘制前跳过的值的个数
//count:跳过偏移量之后要处理的值的个数
//paint:绘制点的画笔
public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
            @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    float[] pts = {50,50,100,200,300,200,200,400};
    canvas.drawPoints(pts,0,8,paint);
}

再来看另外一个重载方法如下:

//pts:要绘制的点的数组[x0,y0,x1,y1,x2,y2...]
//paint:绘制点的画笔
public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    float[] pts = {50,50,100,200,300,200,200,400};
    canvas.drawPoints(pts,paint);
}

2.13 drawRGB

此方法使用RGB颜色填充整个画布的位图,方法如下:

//r:颜色的red部分,取值0--255
//g:颜色的green部分,取值0--255
//b:颜色的blue部分,取值0--255
public void drawRGB(int r, int g, int b)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawRGB(200,100,100);
}

2.14 drawRect

此方法使用画笔绘制矩形,矩形被填充或者显示轮廓由画笔的样式确定,方法如下:

//left:矩形的左边
//top:矩形的顶边
//right:矩形的右边
//bottom:矩形的底边
//paint:绘制矩形的画笔
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawRect(50,100,500,300,paint);
}

看一个重载方法如下:

//r:要绘制的矩形
//paint:绘制矩形的画笔
public void drawRect(@NonNull Rect r, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Rect rect = new Rect(50,100,500,300);
    canvas.drawRect(rect,paint);
}

再来看另外一个重载方法如下:

//rect:要绘制的矩形
//paint:绘制矩形的画笔
public void drawRect(@NonNull RectF rect, @NonNull Paint paint)

此方法的示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(50,100,500,300);
    canvas.drawRect(rectF,paint);
}

2.15 drawRoundRect

此方法使用画笔绘制圆角矩形,矩形被填充或者显示轮廓由画笔的样式确定,方法如下:

//rect:圆角矩形的矩形边界
//rx:圆角的x半径
//ry:圆角的y半径
//paint:绘制圆角矩形的画笔
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(50,100,500,300);
    canvas.drawRoundRect(rectF,20,20,paint);
}

2.16 drawText

此方法用来绘制文本,原点在(x,y),原点和画笔paint中的对齐设置有关,方法如下:

//text:被绘制的文本
//x:文本的原点的x坐标
//y:文本基线的y坐标
//paint:绘制文本的画笔,可以进行颜色、大小、样式等设置
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    String text = "Android Canvas";
    canvas.drawText(text,200,300,paint);
}

再看一个重载方法如下:

//text:被绘制的文本
//start:要绘制的文本中第一个字符的索引
//end:(end-1)是要绘制的文本中最后一个字符的索引
//x:文本的原点的x坐标
//y:文本基线的y坐标
//paint:绘制文本的画笔,可以进行颜色、大小、样式等设置
public void drawText(@NonNull String text, int start, int end, float x, float y,
            @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    String text = "Android Canvas";
    canvas.drawText(text,3,11,200,300,paint);
}

2.17 drawTextOnPath

此方法使用画笔paint沿着路径绘制文本,画笔的对齐方式决定从何处沿着路径开始文本的绘制,方法如下:

//text:被绘制的文本
//path:文本应该遵循的路径
//hOffset:沿着路径文本开始位置偏移的距离
//hOffset:文本在路径之上或之下的偏移的距离,可以为正值或负值
//paint:绘制文本的画笔,可以进行颜色、大小、样式等设置
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
            float vOffset, @NonNull Paint paint)

示例如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    String text = "Android Canvas";
    Path path = new Path();
    path.moveTo(50,50);
    path.lineTo(200,100);
    path.lineTo(400,400);
    canvas.drawTextOnPath(text,path,0,0,paint);
}

3.总结

在自定义View的时候,将会经常用到Canvas,因此熟练地掌握和运用这些绘制方法就显得比较重要。使用Canvas可以绘制点、线、矩形、圆、椭圆、文本、路径、位图等各种各样的图形图案,本文详细地介绍了Canvas的各种方法,并给出了示例代码,灵活运用这些方法进行组合,就能绘制出各种各样的图案和效果。