Android 关于 Canvas 你所知道的和不知道的一切,银行软件开发面试题
paint.setStrokeWidth(2);canvas.drawText(-100 * i + "", coo.x - 20 - 100 * i, coo.y + 40, paint);paint.setStrokeWidth(5);canvas.drawLine(coo.x - 100 * i, coo.y, coo.x - 100 * i, coo.y - 10, paint);}//y 正轴文字 for (int i = 1; i < (winSize.y - coo.y) / 50; i++) {paint.setStrokeWidth(2);canvas.drawText(100 * i + "", coo.x + 20, coo.y + 10 + 100 * i, paint);paint.setStrokeWidth(5);canvas.drawLine(coo.x, coo.y + 100 * i, coo.x + 10, coo.y + 100 * i, paint);}//y 负轴文字 for (int i = 1; i < coo.y / 50; i++) {paint.setStrokeWidth(2);canvas.drawText(-100 * i + "", coo.x + 20, coo.y + 10 - 100 * i, paint);paint.setStrokeWidth(5);canvas.drawLine(coo.x, coo.y - 100 * i, coo.x + 10, coo.y - 100 * i, paint);}}
二、Canvas 绘制基础图形(如果觉得简单可跳过)
以前看到一个类有很多方法都有些不耐烦,这么多,怎么记得住。
现在看到一个类有很多方法,--哇,太好了,哈哈,竟然连这方法都有,作者真给力省的我实现了。
Canvas 图形绘制的 API,所有的我分了一下类:如下(颜色、点、线、矩形、类圆、文字、图片、其他)
下面一一介绍:
1.绘制颜色
/**
绘制颜色(注意在画坐标系前绘制,否则后者覆盖)
@param canvas*/private void drawColor(Canvas canvas) {// canvas.drawColor(Color.parseColor("#E0F7F5"));// canvas.drawARGB(255, 224, 247, 245);// 三者等价 canvas.drawRGB(224, 247, 245);}
2.绘制点
/**
绘制点
@param canvas*/private void drawPoint(Canvas canvas) {//绘制点 canvas.drawPoint(100, 100, mRedPaint);////绘制一组点,坐标位置由 float 数组指定(必须是 2 的倍数个)canvas.drawPoints(new float[]{400, 400, 500, 500,600, 400, 700, 350,800, 300, 900, 300}, mRedPaint);}
3.绘制直线
/**
绘制线
@param canvas*/private void drawLine(Canvas canvas) {canvas.drawLine(500, 200, 900, 400, mRedPaint);//绘制一组点,坐标位置由 float 数组指定(必须是 4 的倍数个)canvas.drawLines(new float[]{200, 200, 400, 200,400, 200, 200, 400,200, 400, 400, 400}, mRedPaint);}
4.绘制矩形
/**
绘制矩形
@param canvas*/private void drawRect(Canvas
canvas) {canvas.drawRect(100, 100, 500, 300, mRedPaint);//等价上行// Rect rect = new Rect(100, 100, 500, 300);// canvas.drawRect(rect,mRedPaint);//(左上右下 X 圆角,Y 圆角)canvas.drawRoundRect(100 + 500, 100, 500 + 500, 300, 50, 50, mRedPaint);}
5.绘制类圆
/**
绘制类圆
@param canvas*/private void drawLikeCircle(Canvas canvas) {//绘制圆(矩形边界,画笔)canvas.drawCircle(650, 200, 100, mRedPaint);// canvas.drawOval(100, 100, 500, 300, mRedPaint);//等价上行//绘制椭圆(矩形边界,画笔)RectF rect = new RectF(100, 100, 500, 300);canvas.drawOval(rect, mRedPaint);
RectF rectArc = new RectF(100 + 500, 100, 500 + 500, 300);//绘制圆弧(矩形边界,开始角度,扫过角度,使用中心?边缘两点与中心连线区域:边缘两点连线区域)canvas.drawArc(rectArc, 0, 90, true, mRedPaint);
RectF rectArc2 = new RectF(100 + 500 + 300, 100, 500 + 500 + 300, 300);//绘制圆弧(矩形边界,开始角度,扫过角度,使用中心?边缘两点与中心连线区域:边缘两点连线区域)canvas.drawArc(rectArc2, 0, 90, false, mRedPaint);}
6.绘制图片
/**
绘制图片
@param canvas*/private void drawBitmap(Canvas canvas) {//1.定点绘制图片 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg);canvas.drawBitmap(bitmap, 100, 100, mRedPaint);//2.适用变换矩阵绘制图片 Matrix matrix = new Matrix();//设置变换矩阵:缩小 3 倍,斜切 0.5,右移 150,下移 100matrix.setValues(new float[]{1, 0.5f, 1500 * 3,0, 1, 100 * 3,0, 0, 3});canvas.drawBitmap(bitmap, matrix, mRedPaint);
//3.图片适用矩形区域不剪裁 RectF rectf1 = new RectF(100 + 900, 100, 600 + 900, 400);canvas.drawBitmap(bitmap, null, rectf1, mRedPaint);
//4.图片裁剪出的矩形区域 Rect rect = new Rect(300, 300, 400, 400);//图片适用矩形区域 RectF rectf2 = new RectF(100 + 900, 100 + 400, 600 + 900, 400 + 400);canvas.drawBitmap(bitmap, rect, rectf2, mRedPaint);}
7.绘制 Picture
1).一开始挺纳闷 Picture 不就是图片吗?然后翻了一下 API:
/**
A Picture records drawing calls (via the canvas returned by beginRecording)
and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or
{@link Canvas#drawPicture(Picture)}).For most content (e.g. text, lines, rectangles),
drawing a sequence from a picture can be faster than the equivalent API
calls, since the picture performs its playback without incurring any
method-call overhead.
<p class="note"><strong>Note:</strong> Prior to API level 23 a picture cannot
be replayed on a hardware accelerated canvas.</p>
一个 Picture 类记录绘制(通过 beginRecording 方法返回的 Canvas),可以展示这个 Canvas 到其他 Canvas 上(通过 Picture#draw(Canvas)或者 Canvas#drawPicture(Picture)),对于大多数的内容,从 picture 绘制都要比相应的 API 要快速,因为 picture 的展现不会招致方法调用开销在 API 级别 23 之前,无法在硬件加速画布上展示 Picture(自译,仅供参考)
2).通过一段代码你就能很清楚它是干嘛用的
如果绘制一个品字,需要这样:
private void drawPicture(Canvas canvas) {canvas.drawRect(100, 0, 200, 100, mRedPaint);canvas.drawRect(0, 100, 100, 200, mRedPaint);canvas.drawRect(200, 100, 300, 200, mRedPaint);}
如果想要复用这个品字形,大多数人知道,平移画布,复制粘贴,
对于少量的代码,这还可以接收,如果是非常复杂的图形,每次绘制重复的内容,会浪费性能
private void drawPicture(Canvas canvas) {//创建 Picture 对象 Picture picture = new Picture();//确定 picture 产生的 Canvas 元件的大小,并生成 Canvas 元件 Canvas recodingCanvas = picture.beginRecording(canvas.getWidth(), canvas.getHeight());//Canvas 元件的操作 recodingCanvas.drawRect(100, 0, 200, 100, mRedPaint);recodingCanvas.drawRect(0, 100, 100, 200, mRedPaint);recodingCanvas.drawRect(200, 100, 300, 200, mRedPaint);//Canvas 元件绘制结束 picture.endRecording();
canvas.save();canvas.drawPicture(picture);//使用 picture 的 Canvas 元件 canvas.translate(0, 300);picture.draw(canvas);//同上:使用 picture 的 Canvas 元件 canvas.drawPicture(picture);canvas.translate(350, 0);canvas.drawPicture(picture);canvas.restore();
Picture 相当于先拍一张照片,并且是在别的 Canvas 上,在别的 Canvas 上,在别的 Canvas 上!
重要的话说三遍:当需要的时候在贴在当前的 canvas 上,picture 绘制的优势就是节能减排
当有大量复杂内容需要复用,Picture 这个的 canvas 元件是不二的选择:
8.绘制文字(文字的效果有 Paint 决定,细节将在 Paint 篇介绍)
/**
绘制文字
@param canvas*/private void drawText(Canvas canvas) {mRedPaint.setTextSize(100);canvas.drawText("张风捷特烈--Toly", 200, 300, mRedPaint);}
无聊的代码终于敲完了,进入正题。
三、Canvas 的画布变换
以前对 Canvas 的变换很厌倦,现在看了键值是神技
作为一代 PS 大神的我,理解 Canvas 状态保存与恢复本应易如反掌,为何最近才豁然开朗
1.先看下面的图形:将坐标系原点设为(500,500)
private void stateTest(Canvas canvas) {canvas.drawLine(mCoo.x + 500, mCoo.y + 200, mCoo.x + 900, mCoo.y + 400, mRedPaint);// canvas.rotate(45);canvas.drawRect(mCoo.x + 100, mCoo.x + 100, mCoo.y + 300, mCoo.y + 200, mRedPaint);}
问题来了,想画一个斜 45 度的矩形怎么办?
貌似没有斜矩形的 API,一个一个点找,貌似太麻烦了,我把纸转一下不就行了吗!
纸就是 Canvas,看一下 API,果然有 rotate()方法,怀着忐忑的心情:
private void stateTest(Canvas canvas) {canvas.drawLine(mCoo.x + 500, mCoo.y + 200, mCoo.x + 900, mCoo.y + 400, mRedPaint);canvas.rotate(45);canvas.drawRect(mCoo.x + 100, mCoo.x + 100, mCoo.y + 300, mCoo.y + 200, mRedPaint);}
果然转得天翻地覆
2.图层的概念
PS 中的图层可谓 PS 的精华,它保证了在一个图层中绘制而不会影响到其他的图层
在 Canvas 中每次的 save()都存将先前的状态保存下来,产生一个新的绘图层,
我们可以随心所欲地地画而不会影响其他已画好的图,最后用 restore()将这个图层合并到原图层
这像是栈的概念,每次 save(),新图层入栈(注意可以 save 多次),只有栈顶的层可以进行操作,restore()弹栈
3.旋转画布:rotate()
private void stateTest(Canvas canvas) {canvas.drawLine(mCoo.x + 500, mCoo.y + 200, mCoo.x + 900, mCoo.y + 400, mRedPaint);canvas.drawRect(mCoo.x + 100, mCoo.x + 100, mCoo.y + 300, mCoo.y + 200, mRedPaint);canvas.save();//保存 canvas 状态//(角度,中心点 x,中心点 y)canvas.rotate(45, mCoo.x + 100, mCoo.y + 100);mRedPaint.setColor(Color.parseColor("#880FB5FD"));canvas.drawRect(mCoo.x + 100, mCoo.x + 100, mCoo.y + 300, mCoo.y + 200, mRedPaint);canvas.restore();//图层向下合并}
4.平移画布:translate():写一堆 mCoo,也就是让画布移动一下而已
效果必变,是不是清爽许多
private void stateTest(Canvas canvas) {canvas.save();canvas.translate(mCoo.x, mCoo.y);//将原点平移到坐标系原点 canvas.drawLine(500, 200, 900, 400, mRedPaint);canvas.drawRect(100, 100, 300, 200, mRedPaint);canvas.save();//保存 canvas 状态//(角度,中心点 x,中心点 y)canvas.rotate(45, 100, 100);mRedPaint.setColor(Color.parseColor("#880FB5FD"));canvas.drawRect(100, 100, 300, 200, mRedPaint);canvas.restore();//图层向下合并 canvas.restore();}
5.缩放画布:scale()
private void stateTest(Canvas canvas) {canvas.save();canvas.translate(mCoo.x, mCoo.y);//将原点平移到坐标系原点 canvas.drawLine(500, 200, 900, 400, mRedPaint);canvas.drawRect(100, 100, 300, 200, mRedPaint);canvas.save();//保存 canvas 状态//(角度,中心点 x,中心点 y)canvas.scale(2, 2, 100, 100);mRedPaint.setColor(Color.parseColor("#880FB5FD"));canvas.drawRect(100, 100, 300, 200, mRedPaint);canvas.restore();//图层向下合并 canvas.restore();}
6.斜切画布:scale()
private void stateTest(Canvas canvas) {
canvas.save();canvas.translate(mCoo.x, mCoo.y);//将原点平移到坐标系原点 canvas.drawLine(500, 200, 900, 400, mRedPaint);canvas.drawRect(100, 100, 300, 200, mRedPaint);canvas.save();//保存 canvas 状态 canvas.skew(1f,0f);mRedPaint.setColor(Color.parseColor("#880FB5FD"));canvas.drawRect(100, 100, 300, 200, mRedPaint);canvas.restore();//图层向下合并 canvas.restore();}
7.画布选择保存状态:
public int save (int saveFlags)默认:MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG
ALL_SAVE_FLAG 保存全部状态 CLIP_SAVE_FLAG 过期---仅保存剪辑区(不作为图层)CLIP_TO_LAYER_SAVE_FLAG 过期---仅剪裁区作为图层保存 FULL_COLOR_LAYER_SAVE_FLAG 过期---仅保存图层的全部色彩通道
评论