上篇文章,我们讲述了Paint(画笔)类的。如果你还未了解,那么可以先看看这篇文章,Android 绘图(一) Paint。今天这篇文章,我们来看看Canvas。Canvas 是画布,来响应绘画(Draw)的调用(并将其写入Btmap)。
我们先看看官方文档对Canvas的描述:
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
简单翻译:Canvas 类持有Draw的调用。绘图的时候,你需要4个基本的组件:一个包含像素的Btmap(位图),一个Canvas
提供Draw调用(写入到Btmap中),一个图元(如矩形、路径、文本、位图),和一个画笔(用来描述颜色和样式为绘图)。
Canvas类,前面已经简单描述过。绘制的时候,需要用画笔(Paint)在画布(Canvas)上面绘画(Draw)。
一. 下面列举一些 Canvas(画布)常用的绘制方法,
1 public void drawArc (float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 参数 startAngle 弧的开始的起始角(度) sweepAngle 顺时针测量的弧度(度) useCenter 如果是真,包含圆弧的中心和如果它是描边将关闭。这将是一个楔形。 paint 绘制弧形的画笔 绘制指定的弧形,该圆弧将按比例缩小到指定的椭圆内。
2 public void drawCircle (float cx, float cy, float radius, Paint paint) 参数 cx 绘制的圆中心的x坐标 cy 绘制的圆中心的y坐标 radius 绘制的圆半径 paint 绘制圆的画笔 绘制指定圆使用指定的画笔。如果半径小于等于0,什么都不绘制。圆将被填满或者描线基于画笔的样式。
3 public void drawLine (float startX, float startY, float stopX, float stopY, Paint paint) 参数 startX 线起始点的x坐标 startY 线起始点的y坐标 paint 绘制线的画笔 绘制一条线段使用指定的起点、终点,x,y坐标,使用指定的画笔。注意,一条线默认是实线,所以画笔的样式被忽略。退化线(长度为0)将不是绘制。
4 public void drawOval (float left, float top, float right, float bottom, Paint paint) 绘制指定的椭圆使用指定的画笔。椭圆将被填充或者描线基于画笔的样式。
5 public void drawRGB (int r, int g, int b) 参数 r 在画布上绘制的红色色值,取值范围(0..255). g 在画布上绘制的绿色色值,取值范围(0..255). b 在画布上绘制的蓝色色值,取值范围(0..255). 填充全部的画布的位图(限于当前剪辑)使用指定的RGB颜色。
6 public void drawRect (float left, float top, float right, float bottom, Paint paint) 参数 left 要绘制的矩形的左边 top 要绘制的矩形的上边 right 要绘制的矩形的右边 bottom 要绘制的矩形的下边 paint 绘制区域的使用的画笔 在指定的区域绘制使用指定的画笔。矩形将被填充或者描边基于画笔的样式。
7 public void drawRect (RectF rect, Paint paint) 参数 rect 绘制的区域 paint 绘制矩形的画笔 在指定的区域绘制使用指定的画笔。矩形将被填充或者描边基于画笔的样式。
8 public void drawRect (Rect r, Paint paint) 参数 r 绘制的矩形 paint 绘制矩形的画笔 在指定的区域绘制使用指定的画笔。矩形将被填充或者描边基于画笔的样式。
ps:
1. RectF类。
RectF holds four float coordinates for a rectangle. The rectangle is represented by the coordinates of its 4 edges (left, top, right bottom). These fields can be accessed directly.
Use width() and height() to retrieve the rectangle‘s width and height. Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
RectF 这个类包含一个矩形的四个单精度浮点坐标。矩形通过上下左右4个边的坐标来表示一个矩形。这些坐标值属性可以被直接访问,用width()和 height()方法可以获取矩形的宽和高。注意:大多数方法不会检查这些坐标分类是否错误(也就是left<=right和top<=bottom).
RectF一共有四个构造方法:
RectF()构造一个无参的矩形
RectF(float left,float top,float right,float bottom)构造一个指定了4个参数的矩形
RectF(Rect F r)根据指定的RectF对象来构造一个RectF对象(对象的左边坐标不变)
RectF(Rect r)根据给定的Rect对象来构造一个RectF对象
2. Rect类。
Rect holds four integer coordinates for a rectangle. The rectangle is represented by the coordinates of its 4 edges (left, top, right bottom). These fields can be accessed directly.
Use width() and height() to retrieve the rectangle‘s width and height. Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
Rect 这个类包含一个矩形的四个整形坐标。矩形通过上下左右4个边的坐标来表示一个矩形。这些坐标值属性可以被直接访问,用width()和 height()方法可以获取矩形的宽和高。注意:大多数方法不会检查这些坐标分类是否错误(也就是left<=right和top<=bottom).
3. 二者的区别。
Rect类,这个类同RectF类很相似,不同的地方是Rect类的坐标是用整形表示的,而RectF类的坐标是用单精度浮点型表示的。
9 public void drawText (String text, float x, float y, Paint paint) 参数 text 绘制的文本 x 绘制文本的原点的x坐标 y 绘制文本的基线的y坐标 paint 用于绘制文本的画笔。(例如颜色、大小、样式) 绘制文本,原点坐标(x,y),使用指定的画笔。原点显示是基于画笔设置的对齐方式。
10 public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) 参数 bitmap 绘制位图 matrix 绘制位图时使用的矩阵 paint 可能为控制。绘制位图使用的画笔 使用指定的矩阵绘制位图
11 public boolean clipPath (Path path) 参数 path 与当前剪辑相交的路径 返回值 如果返回真,表示结果非空 指定的路径与当前剪辑相交
12 public boolean clipRect (Rect rect, Region.Op op) 参数 rect 与当前剪辑相交的矩形 op 如何剪辑 返回值 如果返回真,表示剪辑结果非空 修建当前剪辑和指定的矩形,在局部坐标中表示。
13 public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) 参数 bitmap 绘制的位图 matrix 当绘制位图时需要转变时使用的矩阵 paint 可能为空.绘制位图的画笔. 使用指定的矩阵绘制位图
14 public void drawBitmap (Bitmap bitmap, float left, float top, Paint paint) 参数 bitmap 绘制的位图 left 开始绘制位图时的左侧位置 top 开始绘制位图时的上面位置 paint 可能为空.绘制位图的画笔 绘制指定的位图,它的上/左包含在内,使用指定的画笔,通过当前矩阵变换。
15 public void translate (float dx, float dy) 参数 dx x轴移动的距离 dy y轴移动的距离 画布平移。基于上次移动后的点为原点。初始值原点坐标为(0,0)
例子:画布原点假如落在(0,0),那么translate(10,10)就是在原点(0,0)基础上分别在x轴、y轴移动10,则原点变为(10,10),再次调用translate(10,10),那么原点变为(20,20)。
16 public int save () 返回值 The value to pass to restoreToCount() to balance this save() 该值传递到restoreToCount()中权衡save() 保存当前的矩阵和切片到一个私有的栈中。随后调用平移、缩放、旋转、倾斜、连接或者剪辑,都将照常操作,但是当调用restore()后,所有的都将被遗弃,恢复到save()之前存在的状态。
17 public void restore () 恢复画布到save()之前的状态
ps:当我们对画布进行旋转,缩放,平移等操作的时候其实我们是想对特定的元素进行操作,比如图片,一个矩形等,但是当你用canvas的方法来进行这些操作的时候,其实是对整个画布进行了操作,那么之后在画布上的元素都会受到影响,所以我们在操作之前调用canvas.save()来保存画布当前的状态,当操作之后取出之前保存过的状态,这样就不会对其他的元素进行影响。
18 public void scale (float sx, float sy) 参数 sx X缩放的数值 sy Y缩放的数值 使用当前矩阵对画布进行缩放
19 public final void scale (float sx, float sy, float px, float py) 参数 sx X缩放的数值 sy Y缩放的数值 px 中心点的x坐标(未缩放) py 中心点的y坐标(未缩放) 使用当前矩阵对画布进行缩放
ps:从源码上可以非常清楚的看出scale (float sx, float sy, float px, float py)和scale(float sx , float sy)的差别:
public final void scale(float sx, float sy, float px, float py) {
translate(px, py);
scale(sx, sy);
translate(-px, -py);
}
即先将画布平移px,py,然后scale,scale结束之后再将画布平移回原基准点。
以上就是Canvas (画布)的常用Api,说了这么多,可能还是有些抽象,下面,我们通过实际的例子来演示方法。
二. 例子。
1.新建Android项目。
2.自定义View。
public class View1 extends View { private Paint mPaint = new Paint(); private int DEFAULT_STROKE_WIDTH = 10;// strokeWidth private int DEFAULT_RADIUS = 30;// 半径 private int DEFAULT_TEXT_SIZE = 20;// private int mStrokeWidth = dp2px(DEFAULT_STROKE_WIDTH); private int mRadius = dp2px(DEFAULT_RADIUS); private int mTextSize = sp2px(DEFAULT_TEXT_SIZE); RectF rect;// 矩形 public View1(Context context) { this(context, null); // TODO Auto-generated constructor stub } public View1(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public View1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub // 绘制一条线段 mPaint.setColor(Color.RED);// 设置画笔颜色为白色 mPaint.setAntiAlias(true);// 坑锯齿 mPaint.setStrokeWidth(mStrokeWidth);// 设置画笔的宽度 canvas.drawLine(0, 0, 400, 0, mPaint);// 绘制一条线段 // 绘制空心圆 mPaint.setColor(Color.GRAY);// 设置画笔颜色为蓝色 mPaint.setAntiAlias(true);// 坑锯齿 mPaint.setStyle(Style.STROKE);// 样式是描边 canvas.drawCircle(100, 100, mRadius, mPaint);// 绘制圆 // 绘制矩形 mPaint.setColor(Color.GREEN);// 设置画笔颜色为绿色 mPaint.setStyle(Style.FILL);// 样式是填充 rect = new RectF(200, 200, 300, 300);// 初始化矩形 canvas.drawRect(rect, mPaint); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.setting); canvas.drawBitmap(bitmap, 400, 400, mPaint); // 绘制文字 mPaint.setColor(Color.BLACK);// 设置画笔颜色为绿色 mPaint.setTextSize(mTextSize);// 设置画笔文字的大小 canvas.drawText("50", 100, 500, mPaint);// 绘制文字 } /** * @param dpVal * @return */ private int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } /** * @param spVal * @return */ private int sp2px(int spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics()); } }
效果截图,如下所示,
3.自定义View,展示画布平移。
public class View3 extends View { private Paint mPaint = new Paint(); private int DEFAULT_STROKE_WIDTH = 10;// strokeWidth private int DEFAULT_RADIUS = 30;// 半径 private int DEFAULT_TEXT_SIZE=20;// private int mStrokeWidth = dp2px(DEFAULT_STROKE_WIDTH); private int mRadius = dp2px(DEFAULT_RADIUS); private int mTextSize=sp2px(DEFAULT_TEXT_SIZE); RectF rect;// 矩形 public View2(Context context) { this(context, null); // TODO Auto-generated constructor stub } public View2(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public View2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub // 绘制一条线段 mPaint.setColor(Color.RED);// 设置画笔颜色为白色 mPaint.setAntiAlias(true);// 坑锯齿 mPaint.setStrokeWidth(mStrokeWidth);// 设置画笔的宽度 rect = new RectF(0, 0, 200, 200);//初始化矩形 canvas.drawRect(rect, mPaint); canvas.translate(50, 50); mPaint.setColor(Color.BLACK);// 设置画笔颜色为白色 canvas.drawRect(rect, mPaint); canvas.translate(50, 50); mPaint.setColor(Color.BLUE);// 设置画笔颜色为白色 canvas.drawRect(rect, mPaint); } /** * @param dpVal * @return */ private int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } /** * @param spVal * @return */ private int sp2px(int spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics()); } }
效果截图,
可以看出,每次调用平移后,都是在上次平移后叠加的!
4.自定义View,展示画布缩放。
public class View3 extends View { private Paint mPaint = new Paint();// 画笔 private int DEFAULT_STROKE_WIDTH = 10;// strokeWidth private int mStrokeWidth = dp2px(DEFAULT_STROKE_WIDTH); private int TOTAL_SQUARE_COUNT = 20;// 矩形个数 private int mWidth = 0;// 宽度 private int mHalfWidth = 0;// 宽度一半 RectF rect;// 矩形 public View3(Context context) { this(context, null); // TODO Auto-generated constructor stub } public View3(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public View3(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub setBackgroundColor(Color.BLACK);// 设置背景色 } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub mWidth = getMeasuredWidth();// 获取宽度 mHalfWidth = mWidth / 2;// mPaint.setColor(Color.RED);// 设置画笔颜色为白色 mPaint.setAntiAlias(true);// 坑锯齿 mPaint.setStyle(Style.STROKE);// 样式是描边 mPaint.setStrokeWidth(mStrokeWidth);// 设置画笔的宽度 rect = new RectF(0, 0, mWidth, mWidth);//初始化矩形 drawSquare(canvas); } /** * 绘制图案 * * @param canvas */ private void drawSquare(Canvas canvas) { for (int i = 0; i < TOTAL_SQUARE_COUNT; i++) { canvas.save(); float fraction = (float) i / TOTAL_SQUARE_COUNT; canvas.scale(fraction, fraction, mHalfWidth, mHalfWidth); canvas.drawRect(rect, mPaint); canvas.restore(); } } /** * @param dpVal * @return */ private int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } /** * @param spVal * @return */ private int sp2px(int spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics()); } }
效果截图如下,
调用scale (float sx, float sy, float px, float py),某一个图形按照某一个缩放比例缩放。
三. 总结。
相信大家在自定义View中,或多或少都会见到上述Canvas(画布)Api的调用。如果你对自定义View有所顾虑、担心,推荐看看Hongyang的博客以及慕课网的视频教程,相信肯定对你有帮忙!本人水平有限,如有错误,欢迎指出!