组件的绘制

简介

针对一些复杂组件的绘制,比如报表,往往需要通过绘制及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)的实现。图层使用栈结构管理的:

组件的绘制_第1张图片
20110529003.png

默认就是一个图层。通过调用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,新图层绘制的图像复制到默认图层上。

组件的绘制_第2张图片
Paste_Image.png

这里要注意的是,新建图层后,绘制操作都是在新图层上,在这里例子中,新建的图层选取起点为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。对应的效果图:

组件的绘制_第3张图片
2012120920174170.jpg

可以实现类似橡皮等复杂的效果。
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绘图知识,我们来实现一个圆形头像的效果

组件的绘制_第4张图片
Paste_Image.png

这里实现的方案就是利用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

你可能感兴趣的:(组件的绘制)