Android面试知识自定义控件

Android自定义View工具:Paint&Canva

2017-01-06  本文已影响751人  饱醉豚我去年买了个表

接上篇,Android自定义View工具:Paint&Canvas(一),上篇主要介绍的是Paint,本篇接着来看Canvas。

Canvas常用方法一览:

Canvas常用方法 备注
绘制颜色
drawARGB 通过设置ARGB值绘制颜色
drawRGB 通过设置RGB值绘制颜色
drawColor 绘制颜色
绘制图形
drawPoint,drawPoints 绘制点,点集合
drawLine,drawLines 绘制线,线集合
drawRect 绘制矩形
drawCircle 绘制圆
drawOval 绘制椭圆
drawArc 绘制弧
画布操作
translate、rotate、scale、save、restore 依次为位移、旋转、缩放、保存画布和恢复画布
drawPath 按路径绘制
canvas.drawARGB(int a, int r, int g, int b)
canvas.drawRGB(int r, int g, int b)
canvas.drawColor(int color) 
canvas.drawColor(int color, PorterDuff.Mode mode)

颜色的四种模式:

颜色模式 备注
ARGB8888 四通道高精度(32位)
ARGB4444 四通道低精度(16位)
RGB565 屏幕默认模式(16位)
Alpha8 仅有透明通道(8位)

ARGB:

类型 备注
A(Alpha) 透明度,取值范围 [0,255],0代表完全透明,255代表完全不透明
R(Red) 红色,取值范围 [0,255],0代表无色,255代表红色
G(Green) 绿色,取值范围 [0,255],0代表无色,255代表绿色
B(Blue) 蓝色,取值范围 [0,255],0代表无色,255代表蓝色

其中ARGB的取值范围均为0255(即16进制的0x000xff)
A 从0x00到0xff表示从透明到不透明。
RGB 从0x00到0xff表示颜色从浅到深。
当RGB全取最小值(0或0x000000)时颜色为黑色,全取最大值(255或0xffffff)时颜色为白色
示例代码:

canvas.drawARGB(255, 0, 0, 0);
canvas.drawARGB(255, 255, 0, 0);
canvas.drawARGB(255, 0, 255, 0);
canvas.drawARGB(255, 0, 0, 255);

效果图分别为:

ARGB.png
canvas.drawPoint(float x, float y, Paint paint) //绘制点
canvas.drawPoints( float[] pts, Paint paint) //绘制多个点

canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //绘制线
canvas.drawLines(float[] pts, Paint paint)//绘制多条线

canvas.drawRect(float left, float top, float right, float bottom, Paint paint) //绘制矩形
canvas.drawRect(RectF rect, Paint paint) //绘制矩形

canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint) //绘制圆角矩形
canvas.drawRoundRect(RectF rect, float rx, float ry, Paint paint) //绘制圆角矩形

canvas.drawCircle(float cx, float cy, float radius,Paint paint) 绘制圆

canvas.drawOval(float left, float top, float right, float bottom,Paint paint) //绘制椭圆
canvas.drawOval(RectF oval,Paint paint) //绘制椭圆

canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter,Paint paint) //绘制圆弧
canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) ////绘制圆弧

1. 画点:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(15f);
canvas.drawPoint(400, 400, mPaint);
float[] pts = {500, 500, 500, 600};
canvas.drawPoints(pts, mPaint);

drawPoint前两个参数为点的坐标(x,y),而drawPoints方法第一个参数是float[]数组,每两个代表一个坐标,如上面{500, 500, 500, 600}代表两个坐标点(500,500)和(500,600),效果图:

point.png
2. 画线:
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(15f);
canvas.drawLine(200, 500, 800, 500, mPaint);
float[] pts = {200, 200, 500, 500,
               800, 200, 500, 500};
canvas.drawLines(pts, mPaint);

drawLine前四个参数代表起始坐标(x1,y1)和终点坐标(x2,y2),而drawLines第一个参数是float[]数组,每四个代表一组起始坐标和终点坐标的组合,如上图{200, 200, 500, 500,800, 200, 500, 500}代表两组坐标点:起始坐标(200,200)终点坐标(500, 500),以及起始坐标(800, 200)和终点坐标(500, 500),效果图:

line.png

3. 画矩形:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
//1.直接通过坐标画矩形
canvas.drawRect(200, 200, 1000, 600, mPaint);
//2.通过Rect 画矩形
Rect mRect = new Rect(200, 650, 1000, 1050);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawRect(mRect, mPaint);
//3.通过RectF 画矩形
RectF mRectF = new RectF(200, 1100, 1000, 1500);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
canvas.drawRect(mRectF, mPaint);

通过获得四个点的值,即左上角(x1,y1)和右下角(x2,y2)的坐标来确定矩形,效果图:

rectangle.png
4. 画圆:
drawCircle(float cx, float cy, float radius, Paint paint)

(cx,cy)是圆点坐标,radius是圆半径,示例:

Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10F);
mPaint.setColor(Color.RED);
canvas.drawCircle(600, 600, 200, mPaint);

效果图:

circle.png
5. 画椭圆:
drawOval( RectF oval,Paint paint)
drawOval(float left, float top, float right, float bottom, Paint paint)

第一个方法看源码还是调用的第二个方法,示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(10f);
//第一种,通过left top right bottom来确定矩形,然后画矩形的内切椭圆
canvas.drawOval(300, 100, 900, 500, mPaint);
RectF mRectF = new RectF(100, 600, 1000, 900);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
//第二种,通过RectF 画椭圆,和第一种原理是一样的
canvas.drawOval(mRectF, mPaint);

效果图:

oval.png
6. 画圆角矩形:
//第一个方法必须SDK>=21以上
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
drawRoundRect(RectF rect, float rx, float ry, Paint paint)

示例:

float rRadius=20f;
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
//第一种 必须SDK>=21以上使用
canvas.drawRoundRect(200, 100, 1000, 500, rRadius, rRadius, mPaint);
//第二种通过RectF 来画圆角矩形
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
RectF mRect = new RectF(200, 600, 1000, 1000);
canvas.drawRoundRect(mRect, rRadius, rRadius, mPaint);

RectF构造函数中的四个参数分别为上,左,右,下坐标,即左上角和右下角坐标,rx,ry表示的圆弧半径(椭圆圆弧),效果图:

roundrectangle.png
7. 画弧:
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, Paint paint)

第一个方法默认也会去调用第二个方法,看第二个方法:

drawArc 备注
left 左上角坐标的X轴坐标
top 左上角坐标的Y轴坐标
right 右下角坐标X轴坐标
bottom 右下角坐标Y轴坐标
startAngle 起始角度
sweepAngle 扫过的角度
useCenter 是否使用中心
paint 画笔

示例:

Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(10f);
//通过下面前两个圆弧对比很明显,useCenter为true,最后图形是一个扇形,
//useCenter为false,则最后图形是起始点和结束点之间的连线和圆弧围成的面积。
canvas.drawArc(100, 100, 500, 500, 0, 90, true, mPaint);
canvas.drawArc(100, 500, 500, 900, 0, 90, false, mPaint);
//通过RectF 设置的区域范围画弧
RectF mRectF = new RectF(100, 900, 500, 1300);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(mRectF, 0, 90, true, mPaint);

效果图:


arc.png
canvas.translate(float dx, float dy) //平移
canvas.rotate(float degrees) //旋转
canvas.rotate(float degrees, float px, float py) //改变旋转中心并旋转
canvas.scale(float sx, float sy) //缩放
canvas.scale(float sx, float sy, float px, float py) //改变缩放中心并缩放
canvas.save(); //保存画布
canvas.restore(); //恢复画布

Canvas画布操作可以让我们用更容易的方式来绘制图形,Canvas画布操作只会对后面的绘制起作用,对前面已经绘制的不起作用。
1. translate
translate是对坐标系的平移,且是可以多次叠加的,默认是在屏幕的左上角(0,0),示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
//将坐标系移到Canvas宽度的一半,高度400的位置
canvas.translate(getMeasuredWidth() / 2, 400);
//绘制一个红色圆
mPaint.setColor(Color.RED);
canvas.drawCircle(0, 0, 100, mPaint);
//坐标系原点在前面位置的基础上再往下移动200像素
canvas.translate(0, 200);
//绘制一个黄色圆
mPaint.setColor(Color.YELLOW);
canvas.drawCircle(0, 0, 100, mPaint);
//坐标系原点在前面位置的基础上再往下移动200像素
canvas.translate(0, 200);
//绘制一个绿色圆
mPaint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 100, mPaint);

效果图:

translate.png

2. rotate
rotate是对坐标系的旋转,rotate有两个方法:

canvas.rotate(float degrees) 
canvas.rotate(float degrees, float px, float py) 

第二个方法中的(px,py)可以移动旋转中心然后旋转,示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
//将坐标系移动到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
RectF rectF = new RectF(-300, -300, 0, 0);
//绘制一个红色矩形
mPaint.setColor(Color.RED);
canvas.drawRect(rectF, mPaint);
//将坐标系旋转180度,不会影响前面已经绘制的图形
canvas.rotate(180);
mPaint.setStyle(Paint.Style.FILL);
//绘制一个绿色矩形
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);

效果图:


rotate.png

上面代码如果改变旋转中心再旋转:

//将坐标系中心左移(-150,0),并旋转180度
canvas.rotate(180,-150,0);

效果图:

rotateCenter.png

可以旋转中心向左移了150像素,而原来的旋转中心已经不再是(0,0)了,而成了(150,0),旋转中心变了,旋转后绘制的图形位置也就不一样了。

3. scale

canvas.scale(float sx, float sy)
canvas.scale(float sx, float sy, float px, float py)

scale是缩放,缩放中心默认是坐标原点,且多次缩放是可以叠乘的,和rotate一样有2个方法,其中多出来的两个参数类似rotate的旋转中心,px,py是控制缩放中心位置,sx,sy是X轴和Y轴的缩放倍数,示例:

Paint mPaint = new Paint();
//坐标原点移到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
RectF rectF = new RectF(-300, -300, 0, 0);
mPaint.setColor(Color.RED);
//左上角绘制红色圆
canvas.drawRect(rectF, mPaint);
//X轴 Y轴分别缩放到原来的1/2并以原点(0,0)位对称点进行翻转
canvas.scale(-0.5f, -0.5f);
//绘制绿色的圆
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);

效果图:


scale1.png

修改上面scale方法:

canvas.scale(-0.5f, -0.5f, 100, 0)

效果图:


scale2.png

缩放中心向右移动100像素,X轴 Y轴分别缩放到原来的1/2,并且以新的缩放中心为对称点进行翻转,在修改一下scale参数:

canvas.scale(0.5f, 0.5f);
canvas.scale(-0.5f, 0.5f);
canvas.scale(0.5f, -0.5f);

效果图分别为:

initpintu.jpg
总结一下sx sy
sx sy取值范围 备注
(1,+∞) 根据缩放中心放大到原来的n倍
1 跟原来大小一样,没变化
(0,1) 根据缩放中心缩放
0 sx sy有一个取0图形就消失了
(-1,0) 根据缩放中心缩放并翻转
-1 翻转
(-∞,-1) 根据缩放中心放大并翻转

4. save、restore:

save:保存之前Canvas的状态,save之后,可以调用Canvas的平移、缩放、旋转、错切、裁剪等操作。
restore:恢复Canvas之前保存的状态,防止save后对Canvas执行的操作对后续的绘制有影响。
canvas的save 和 restore是成对使用(restore只能比save少,不能多),示例:

Paint paint = new Paint();
//保存画布
canvas.save();
//坐标原点移到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//以屏幕中心为坐标原点在(60,50)为圆心处绘制红色圆
paint.setColor(Color.RED);
canvas.drawCircle(60, 50, 100, paint);
//恢复画布
canvas.restore();
//恢复画布后,坐标原点(0,0)默认在屏幕左上角,
//即以屏幕左上角为坐标原点在(60,50)为圆心处绘制黑色圆
paint.setColor(Color.BLACK);
canvas.drawCircle(60, 50, 50, paint);

效果图:


保存恢复画布.png

上面代码中如果去掉save和restore过程,即:

Paint paint = new Paint();
//canvas.save();
//坐标原点移到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//以屏幕中心为坐标原点在(60,50)为圆心处绘制红色圆
paint.setColor(Color.RED);
canvas.drawCircle(60, 50, 100, paint);
//canvas.restore();
//没有了save和restore操作,坐标原点没有恢复到屏幕左上角,还是在屏幕中心
//即以屏幕中心为坐标原点在(60,50)为圆心处绘制黑色圆
paint.setColor(Color.BLACK);canvas.drawCircle(60, 50, 50, paint);

效果图是:


nosave.png

为什么效果不一样呢?这是因为如果去掉save和restore过程,所有图像都是在坐标原点移动到屏幕中心后绘制的;如果有save和restore,那么在restore之后进行的图像绘制原点又回到了屏幕左上角,可见save和restore调用时机会影响到最后绘图结果,save和restore之间往往是进行一系列的Canvas操作。

canvas.drawPath( Path path, Paint paint)

drawPath内容较多,见下篇:Android Canvas之Path操作

上一篇下一篇

猜你喜欢

热点阅读