简介
针对一些复杂组件的绘制,比如报表,往往需要通过绘制及onDraw(Canvas canvas)方法来实现,因此需要熟练掌握canvas的相关实现。
Canvas常用api
操作类型 | 相关API | 备注 |
---|---|---|
绘制颜色 | drawColor, drawRGB, drawARGB | 使用单一颜色填充整个画布 |
绘制基本形状 | drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc | 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧 |
绘制图片 | drawBitmap, drawPicture | 绘制位图和图片 |
绘制文本 | drawText, drawPosText, drawTextOnPath | 依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字 |
绘制路径 | drawPath | 绘制路径,绘制贝塞尔曲线时也需要用到该函数 |
顶点操作 | drawVertices, drawBitmapMesh | 通过对顶点操作可以使图像形变,drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用 |
画布剪裁 | clipPath, clipRect | 设置画布的显示区域 |
画布快照 | save, restore, saveLayerXxx, restoreToCount, getSaveCount | 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数 |
画布变换 | translate, scale, rotate, skew | 依次为 位移、缩放、 旋转、倾斜 |
Matrix(矩阵) | getMatrix, setMatrix, concat | 实际上画布的位移,缩放等操作的都是图像矩阵Matrix, 只不过Matrix比较难以理解和使用,故封装了一些常用的方法。 |
Canvas重点知识点理解
1、Canvas的图层
canvas通常认为就是一张画布,通过调用一些api在这张画布上绘制,如果要实现一些复杂的绘图操作,比如地图(由多层的地图叠加,如:底图、道路、兴趣点),canvas提供了图层(Layer)的实现。图层使用栈结构管理的:
默认就是一个图层。通过调用saveLayer或saveLayerAlpha在栈中添加一个图层,调用restore或restoreToCount,从栈中退出一个图层,退栈时,会把本图层绘制的图像复制到上层或canvas上。
canvas.drawColor(Color.WHITE);canvas.translate(10, 10);
mPaint.setColor(Color.RED);canvas.drawCircle(75, 75, 75, mPaint);
canvas.saveLayerAlpha(50, 50, 200, 200, 0x88, Canvas.ALL_SAVE_FLAG);
mPaint.setColor(Color.BLUE);canvas.drawRect(0,0,150, 150, mPaint);
canvas.restore();
在默认图层绘制了一个红色的圆,新建一个图层透明度为0x88,绘制一个蓝色的矩形,调用restore,新图层绘制的图像复制到默认图层上。
这里要注意的是,新建图层后,绘制操作都是在新图层上,在这里例子中,新建的图层选取起点为x=50,y=50,长宽为200的区域,因此再绘制一个蓝色矩形canvas.drawRect(0,0,150, 150, mPaint);相对原来的图层也是起点为x=50,y=50。
2、画布的变化与Matrix(矩阵)
通过调用canvas的translate, scale, rotate, skew方法可以实现位移、缩放、 旋转、倾斜,这些画布的变化都是相对一个坐标原点的,默认的坐标原点是视图的左上角,x=0,y=0,通过translate可以改变坐标原点,如果你想实现在两个手指触摸的中心点进行图片的旋转或缩放,则需要把原点移动到中心点,或是调用方法的时候传入中心点的位置。
canvas的这些变化实际上就是他的Matrix的变化,可以通过getMatrix获取到对对应视图的Matrix,在调用Matrix 的 translate, scale, rotate, skew方法对矩阵进行变化,再调用concat把变化的举证与原来的矩阵叠加,返回给canvas,画布就变化了。(setMatrix和concat对矩阵的变化是不一样的,concat与canvas的变化方法类似,都是针对坐标原点的,setMatrix则会把所设置的矩阵当做相对于整个屏幕(包括系统栏))
3、图像渲染(Shader)
android提供了Shader类,用来渲染图像,包括BitmapShader、ComposeShader、LinearGradient、RadialGradient以及SweepGradient。
使用图像渲染时,需要创建一个shader类,通过paint的setShader方法设置,最后将paint绘制到画布上即可。
1、BitmapShader(图像渲染)
使用一张位图作为纹理来对某一区域进行填充。
2、LinearGradient(线性渲染)
实现某一区域内颜色的线性渐变效果。
3、ComposeShader(混合渲染)
ComposeShader的作用是实现渲染效果的叠加,如BitmapShader与LinearGradient的混合渲染效果等。
4、RadialGradient(环形渲染)
RadialGradient的作用是在某一区域内实现环形的渐变效果。
5、SweepGradient(梯度渲染)
SweepGradient也称为扫描渲染,是指在某一中心以x轴正方向逆时针旋转一周而形成的扫描效果的渲染形式。
详细说明参考下面网址的文章。
这里想重点说下PorterDuff.Mode,表示两个效果叠加的模式,有16种可供选择,分别为:CLEAR、SRC、DST、SRC_OVER、DST_OVER、SRC_IN、DST_IN、SRC_OUT、DST_OUT、SRC_ATOP、DST_ATOP、XOR、DARKEN、LIGHTEN、MULTIPLY、SCREEN。对应的效果图:
可以实现类似橡皮等复杂的效果。
PorterDuff.Mode可以用在
ComposeShader中:
public ComposeShader (Shader shaderA, Shader shaderB, PorterDuff.Mode mode);
也可以用在canvas.drawColor
public void drawColor( int color, PorterDuff.Mode mode) ,drawColor(int color)使用的是src_over模式
public void drawColor(@ColorInt int color) {
native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
}
实例
结合上面学到的canvas绘图知识,我们来实现一个圆形头像的效果
这里实现的方案就是利用BitmapShader来实现
初始化时获取要显示的头像,并创建bitmapshader
private void init() {
imgBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.img)).getBitmap();
mBitmapHeight = imgBitmap.getHeight();
mBitmapWidth = imgBitmap.getWidth(); //创建Bitmap渲染对象
bitmapShader = new BitmapShader(imgBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
绘制的时候把shader设置给paint,并画一个圆
protected void onDraw(Canvas canvas) {
Paint mPaint = new Paint();
canvas.drawColor(Color.TRANSPARENT);
mPaint.setShader(bitmapShader);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint);
}
但是你会发现展示的效果是头像显示不全或没有填满,那是因为还要对图片进行缩放。
创建一个矩阵,更具组件的宽高缩放对应的矩阵,设置给bitmapShader
private void setUp() {
//等比例压缩图片
mShaderMatrix.set(null);
int width = getWidth();
int height = getHeight();
float scale;
float dx = 0;
float dy = 0;
if (mBitmapWidth * height > mBitmapHeight * width) {
scale = height / (float) mBitmapHeight;
dx = (width - mBitmapWidth * scale) * 0.5f;
} else {
scale = width / (float) mBitmapWidth;
dy = (height - mBitmapHeight * scale) * 0.5f;
}
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
bitmapShader.setLocalMatrix(mShaderMatrix);
}
然后在绘制前调用:
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setUp();
}
相关参考网址
http://www.jianshu.com/p/4a911f048e5c
http://ltlovezh.com/2016/04/27/Matrix%E4%BD%BF%E7%94%A8%E8%A7%A3%E6%9E%90/
http://www.cnblogs.com/menlsh/archive/2012/12/09/2810372.html
http://blog.csdn.net/t12x3456/article/details/10432935