1.前言
在Android中如果我们想进行比较复杂的自定义View或者游戏控件,我们就要使用到绘图的API,同时这些也是我们自定义View的基础。
2.实现分析
Android是通过graphics类来显示2D图形,其中graphics中又包括了Canvas、Paint、Colcr、Bitmap等类。graphics是具有绘制点、线、颜色、2D几何图形、图像处理等功能。Android中绘图的三个最基本的类分别为Color、Paint、Canvas,这三个类都存放在android.graphics包下,其中Color表示颜色的类相当于我们现实生活中的“颜料”,Paint表示画笔的类相当于我们现实生活中的“画笔”,Canvas表示画布的类相当于我们现实生活中的“画布”。本节主要为大家介绍Paint和Canvas,这两个类顾名思义就是画笔和画布,在绘制图形的过程中我们只需要用画笔在画布上画出我们想要的图形即可。
(1)Paint类
和我们日常绘画一样要想绘制图形,首先需要选择合适的画笔。同理,在Android中要想绘制图形我们也要有合适的画笔,在Paint类中Android为我们提供了许多的方法,使得我们可以通过这些方法设置画笔相关的属性,从而得到合适的画笔。
setARGB(int a, int r, int g, int b):设置画笔透明度和颜色,其参数分别代表透明度、红色、绿色、蓝色
setColor(int color):设置画笔的颜色
setAlpha(int a):设置画笔的透明度(取值范围为0~255,数值越小越透明)
setAntiAlias(boolean a):设置是否抗锯齿
setPathEffect(PathEffect effect):设置绘制路径时的路径效果
setShader(Shader shader):设置画笔的填充效果
setShadowLayer(float radius, float dx, float dy, int color):设置阴影效果
setStrokeWidth(float width):设置画笔宽度
setStrokeJoin(Paint.Join join):设置画笔转弯处的连接风格(BEVEL-直线、MITER -锐角、ROUND-圆弧 )
setStyle(Paint.Style style):设置Paint的填充风格(FILL-、FILL_AND_STROKE、STROKE )
setTextAlign(Paint.Align align):设置绘制文本时的文字对齐方式(CENTER -居中、LEFT-靠左、RIGHT -靠右:以(x,y)为中心)
setTextSize(float textSize):设置绘制文本的文字大小
在使用的过程中需要先构建对象
Paint paint = new Paint();
在通过设置属性来完成
paint.setXXX();
(2)Canvas类
Canvas绘图有三个基本要素:Canvas、绘图坐标和Paint。其中Canvas是画布,我们通过Canvas类提供的众多drawXXX()方法将需要的图形绘制在画布上(Canvas),在使用这些方法的过程中我们需要传入绘图的坐标和一致画笔(Paint),传入的坐标和Paint决定了要绘制图像的位置和属性而通过哪种方法绘图决定了图片的样子。
Canvas绘图中牵扯到两种坐标:Canvas坐标和绘图坐标
Canvas坐标系:
Canvas坐标系指的是Canvas本身的坐标系,Canvas坐标系有且只有一个,且是唯一不变的,其坐标原点在View的左上角,从坐标原点向右为x轴的正半轴,从坐标原点向下为y轴的正半轴。
绘图坐标系:
Canvas的drawXXX方法中传入的各种坐标指的都是绘图坐标系中的坐标,而非Canvas坐标系中的坐标。默认情况下,绘图坐标系与Canvas坐标系完全重合,即初始状况下,绘图坐标系的坐标原点也在View的左上角,从原点向右为x轴正半轴,从原点向下为y轴正半轴。但不同于Canvas坐标系,绘图坐标系并不是一成不变的,可以通过调用Canvas的translate方法平移坐标系,可以通过Canvas的rotate方法旋转坐标系,还可以通过Canvas的scale方法缩放坐标系,而且需要注意的是,translate、rotate、scale的操作都是基于当前绘图坐标系的,而不是基于Canvas坐标系,一旦通过以上方法对坐标系进行了操作之后,当前绘图坐标系就变化了,以后绘图都是基于更新的绘图坐标系了。也就是说,真正对我们绘图有用的是绘图坐标系而非Canvas坐标系。
在重写onDraw(Canvas canvas)的方法时涉及一个绘图API:Canvas,它就是代表“依附”于指定View的画布,在Canvas类中提供了一系列的方法用来进行坐标变换
translate(float dx, float dy):移动绘图坐标系。参数一表示向右移动的距离,如果为负数即向左移动;参数二表示向下移动的距离,如果为负数即向上移动
scale(float sx, float sy):使用原点(0,0)为默认基准点缩放绘图坐标系。参数一表示x轴缩放的倍数;参数二表示y轴缩放的倍数
scale(float sx, float sy, float px, float py):使用自定义基准点缩放绘图坐标系。参数一表示x轴缩放的倍数;参数二表示y轴缩放的倍数;参数三和参数四表示自定义基准点的坐标
rotate(float degrees):使用原点(0,0)为默认基准点旋转绘图坐标系。参数一表示旋转的角度
rotate(float degrees, float px, float py):使用自定义基准点旋转绘图坐标系。参数一表示旋转的角度;参数二和参数三表示自定义基准点的坐标
skew(float sx, float sy):对绘图坐标系进行倾斜变换。参数一表示将横坐标在x轴方向倾斜相应的角度,sx为倾斜角度的tan值;参数二表示将纵坐标在y轴方向倾斜相应的角度,sy为倾斜角度的tan值(这里要注意,这个方法中的参数全部是倾斜角度的tan值)
下面对于上面的几个方法分别举几个例子,这样可以更好的理解这几个方法中参数的含义
首先进行初始化,先把画布设置为黑色,在画布上画一个360*360的白色矩形
主要代码和效果图如下
package com.example.administrator.canvastest;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by ChuPeng on 2016/10/20.
*/
public class CanvasView extends View
{
private Paint paint;
public CanvasView(Context context)
{
super(context);
}
public CanvasView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public CanvasView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CanvasView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
protected void onDraw(Canvas canvas)
{
paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawColor(Color.BLACK);
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
super.onDraw(canvas);
}
}
此时的效果图如下
接下来我们来使用translate(float dx, float dy)的方法,将绘图坐标系分别向x轴和y轴平移100px,再画出一个360*360蓝色的矩形
修改后的主要代码为
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawColor(Color.BLACK);
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
canvas.translate(100, 100);
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
}
此时的效果图如下
接下来我们来使用scale(float sx , float sy)的方法,以原点为基准点将绘图坐标系的x轴和y轴分别缩放相应的倍数
修改后的代码为
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.WHITE);
//将画布的背景设置为黑色
canvas.drawColor(Color.BLACK);
//在画布上绘制一个白色360*360的矩形
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
//将绘图坐标系沿x轴和Y轴平移100px
canvas.translate(100, 100);
//在画布上绘制一个蓝色360*360的矩形
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
//将绘图坐标系缩放为原来的0.5倍
canvas.scale(0.5f, 0.5f);
//在画布上绘制一个红色360*360的矩形
paint.setColor(Color.RED);
canvas.drawRect(rect, paint);
}
此时的效果图如下
将这两张效果图进行对比可以发现,画矩形的参数没有变化,但是矩形出现在屏幕中的位置却改变了,这是因为translate(float dx, float dy)方法改变了绘图坐标,而Canvas的drawXXX方法是基于绘图坐标进行绘图的,这里的效果就相当于用个钉子钉在(0,0)处,然后把矩形的x,y缩放为一半
接下来我们来使用scale(float sx , float sy, float px,float py)的方法,以自定义点为基准点将绘图坐标系的x轴和y轴分别缩放相应的倍数
修改后的代码为
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.WHITE);
//将画布的背景设置为黑色
canvas.drawColor(Color.BLACK);
//在画布上绘制一个白色360*360的矩形
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
//将绘图坐标系沿x轴和Y轴平移100px
canvas.translate(100, 100);
//在画布上绘制一个蓝色360*360的矩形
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
//保存画布的状态
canvas.save();
//以原点为基准点将绘图坐标系缩放为原来的0.5倍
canvas.scale(0.5f, 0.5f);
//在画布上绘制一个红色360*360的矩形
paint.setColor(Color.RED);
canvas.drawRect(rect, paint);
//画布状态回滚
canvas.restore();
//以自定义点为基准点将绘图坐标系缩放为原来的0.5倍
canvas.scale(0.5f, 0.5f, 180, 180);
//在画布上绘制一个黑色360*360的矩形,并用白色的点表示自定义基准点
paint.setColor(Color.BLACK);
canvas.drawRect(rect, paint);
paint.setColor(Color.WHITE);
}
对比两张图可以发现这里的效果就相当于用个钉子钉在(180,180)处,然后把矩形的x,y缩放为一半
接下来我们使用rotate(float degrees)的方法,以原点为基准点旋转绘图坐标系
修改后的代码为
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.WHITE);
//将画布的背景设置为黑色
canvas.drawColor(Color.BLACK);
//在画布上绘制一个白色360*360的矩形
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
//以原点为基准点将绘图坐标系旋转45度
canvas.rotate(45);
//在画布上绘制一个蓝色360*360的矩形
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
}
此时的效果图如下
此时可以发现蓝色矩形相较于白色矩形以原点为基点旋转了45度
接下来我们使用rotate(float degrees, float px, float py)的方法,以自定义点为基准点旋转绘图坐标系
修改后的代码为
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.WHITE);
//将画布的背景设置为黑色
canvas.drawColor(Color.BLACK);
//在画布上绘制一个白色360*360的矩形
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
//以自定义点为基准点将绘图坐标系旋转45度
canvas.rotate(45, 200, 200);
//在画布上绘制一个蓝色360*360的矩形
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
}
此时效果图如下
接下来我们使用skew(float sx, float sy)的方法,以原点为基准点对绘图坐标系进行倾斜变换。在使用这个方法的过程中要注意此方法中的参数都是tan值。
修改后的代码为
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.WHITE);
//将画布的背景设置为黑色
canvas.drawColor(Color.BLACK);
//在画布上绘制一个白色360*360的矩形
Rect rect = new Rect(0, 0, 360, 360);
canvas.drawRect(rect, paint);
//以原点为基准点对绘图坐标系进行倾斜变换
canvas.skew(1, 0);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
<span style="white-space:pre"> </span>//在画布上沿x轴和y轴画坐标
canvas.drawLine(0, 0, 360, 0, paint);
canvas.drawLine(0, 0, 0, 360, paint);
}
此时的效果图如下
此时通过效果图可以发现绘图坐标系的y轴向x轴方向旋转了45度
Canvas类也给我们提供了众多的方法,使我们可以方便快捷的画出各种图形(在使用drawXXX() 方法族画的图会有图层叠加的情况,即后面绘画的图层会覆盖前面绘画的图层)
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint):在指定的矩形区域内绘制弧。参数一表示RectF的对象表示指定的矩形;参数二表示起始角度;参数三表示结束角度;参数四表示一个布尔类型,如果为true是画椭圆,如果是false是只画圆弧;参数五是Paint的对象表示画笔
drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) :在指定点绘制从源位图中挖取一块的内容。参数一表示我们常规的Bitmap对象;参数二表示源区域(这里是bitmap); 参数三表示目标区域(应该在canvas的位置和大小);参数四是Paint的对象表示画笔; 因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。
drawBitmap (Bitmap bitmap, float left, float top, Paint paint) :在指定点绘制源位图。参数一表示我们常规的Bitmap对象;参数二和参数三表示坐标的位置;参数四是Paint的对象表示画笔
drawCircle (float cx, float cy, float radius,Paint paint):在指定地点绘制圆。参数一和参数二分别表示圆心的x坐标和y坐标;参数三是半径;参数四是Paint的对象表示画笔
drawLine (float startx, float starty, float stopx, float stopy, Paint paint) : 在指定地点绘制一条直线。参数一和参数二分别表示直线起始点的x坐标和y坐标;参数三和参数四分别表示直线终止点的x坐标和y坐标;参数五是Paint的对象表示画笔
drawLines (float[] pts, Paint paint):在指定地点绘制多条直线。参数一表示绘制直线的端点数组,每条直线占用4个数据分别为起始和终止点的坐标;参数三是Paint的对象表示画笔
drawLines (float[] pts, int offset, int count, Paint paint):在指定地点有选择的绘制多条直线。参数一表示绘制直线的端点数组,每条直线占用4个数据分别为起始和终止点的坐标;参数二表示跳过的数据个数,这些数据将不参与绘制过程;参数三表示实际参与绘制的数据个数;参数四是Paint的对象表示画笔
drawOval (RectF oval, Paint paint):在指定的矩形区域内绘制椭圆。参数一表示RectF的对象表示指定的矩形;参数二是Paint的对象表示画笔
drawPath (Path path, Paint paint) :绘制一个路径。参数一表示Path路径对象;
drawPoint(float x, float y, Paint paint):在指定地点绘制一个点。参数一和参数二表示这个点的坐标位置;参数三是Paint的对象表示画笔
drawPoints(float[] pts, int offset, int count, Paint paint):在指定地点有选择的绘制多个点。参数一表示绘制点的坐标数组,每个点占用2个数据分别为横坐标和纵坐标;参数二表示跳过的数据个数,这些数据将不参与绘制过程;参数三表示实际参与绘制的数据个数;参数四是Paint的对象表示画笔
drawRect(float left, float top, float right, float bottom, Paint paint):在指定地点绘制一个矩形。参数一和参数二表示矩形左上角的坐标;参数三和参数四表示矩形右下角的坐标;参数五是Paint的对象表示画笔
drawRoundRect(RectF rect, float rx, float ry, Paint paint):在指定的地点绘制一个圆角矩形。参数一表示RectF的对象表示指定的矩形;参数二表示x方向上的圆角半径;参数三表示y方向上的圆角半径;参数四是Paint的对象表示画笔
drawText(String text, float x, float y, Paint paint):以原点为坐标在指定的地点绘制字符串。参数一表示字符串的内容;参数二和参数三表示原点的坐标;参数四是Paint的对象表示画笔
drawTextOnPath(String text, Path path, float x, float y, Paint paint):以原点为坐标沿着指定的路径绘制字符串。参数一表示字符串的内容;参数二表示Path路径对象;参数三和参数四表示原点的坐标;参数五是Paint的对象表示画笔
3.总结
到目前为止Paint和Canvas两个类中的基本使用方法都已经介绍完毕了,有关于绘图的例子就不在这里进行列举了,因为干巴巴的去画一个圆或者一条直线没有什么实际的意义,在以后的文章中会有实际的绘图的例子。
以上Demo的源代码地址:点击打开链接