Canvas

canvas是一种抽象概念,是2D图形系统中的重要部分,canvas一系列函数最终都是android 2D图形库Skia的一些列封装,对应在SKCanvas.cpp。canvas在系统中的位置如下图所示

可以将canvas看成一个透明的图层,使用canvas之后会产生一个透明图层,然后在这个新图层上画图,画完之后覆盖在屏幕上显示,叠加。

比较经典的例子就是

` protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //构造一个矩形
    Rect rect1 = new Rect(0, 0, 400, 220);
    //在平移画布前用绿色画下边框
    canvas.drawRect(rect1, paint_green);

    //平移画布后,再用红色边框重新画下这个矩形
    canvas.translate(100, 100);
    canvas.drawRect(rect1, paint_red);
}`

我们首先画了一个绿色矩形框,然后将canvas平移了,接着画了一个红色的矩形框,结果如下

这里我们会发现,平移了canvas之后绿色矩形框没发生变化!,拉近镜头我们仔细看看究竟发生了什么,如何印证canvas每次会产生一个透明图层?

调用canvas.drawRect(rect1, paint_green);时,产生一个Canvas透明图层,由于当时还没有对坐标系平移,所以坐标原点是(0,0);再在系统在Canvas上画好之后,覆盖到屏幕上显示出来,过程如下图(图片来自网络,链接在文后):

然后再第二次调用canvas.drawRect(rect1, paint_red);时,又会重新产生一个全新的Canvas画布,此时由于使用tranlate移动了画布

因而在画图时是以新原点来产生视图的,然后合成到屏幕上,超出部分不显示,最后就是我们看到结果了。

canvas常用方法

下面来清点一下重要的API,绘制颜色就不赘述了

绘制基本形状

drawRoundRecf画圆角矩形

`   // 第一种
    RectF rectF = new RectF(100,100,400,400);
    canvas.drawRoundRect(rectF,30,30,paint_red);

    // 第二种 需要api21,一般常用第一种
   // canvas.drawRoundRect(100,100,800,400,30,30,paint_red);`

这里是采用第一种方法画出来的,参数30(rx),30(ry)是椭圆的两个半径,用来确定圆角的弧度。

drawOval画椭圆

`   // 第一种
    RectF rectF = new RectF(100,100,500,400);
    canvas.drawOval(rectF,paint_red);

    // 第二种
    canvas.drawOval(100,100,800,400,paint_red);`

通常也是使用第一种,画椭圆只需要一个矩形即可,椭圆是改矩形的内切

drawArc画圆弧

// 第一种

public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}

// 第二种

public void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

可以看出这里使用了椭圆的形状,除此之外加上了三个参数

startAngle // 开始角度

sweepAngle // 扫过角度

useCenter // 是否和中心点连成一个封闭的图形

`   RectF rectF = new RectF(100, 100, 400, 400);
    RectF rectF1 = new RectF(200, 200, 500, 500);

    canvas.drawArc(rectF, 0, 40, true, paint_green);
    canvas.drawArc(rectF1, 0, 40, false, paint_green);`

画圆弧要清楚,0-40画弧,首先水平那根线是起点,按顺时针40度停止。

drawPath

这个结合path一起使用会非常强大!(path用法也是内涵满满,这里就不展开叙述了)

这里看下canvas中的用法

public void drawPath(@NonNull Path path, @NonNull Paint paint){}

传入路径,和对应的画笔即可

`   Path path = new Path();
    path.moveTo(50,50);
    path.quadTo(30,220,320,450);

    canvas.drawPath(path,paint_green);`

这里使用path画了一条贝塞尔曲线

画布裁剪

裁剪涉及到一个Region.Op区域组合的问题

`    假设用region A  去组合region B
public enum Op {
        DIFFERENCE(0), //A和B的差集范围,即A - B,只有在此范围内的绘制内容才会被显示;
        INTERSECT(1), // 即A和B的交集范围,只有在此范围内的绘制内容才会被显示
        UNION(2),      //即A和B的并集范围,即两者所包括的范围的绘制内容都会被显示;
        XOR(3),        //A和B的补集范围,此例中即A除去B以外的范围,只有在此范围内的绘制内容才会被显示;
        REVERSE_DIFFERENCE(4), //B和A的差集范围,即B - A,只有在此范围内的绘制内容才会被显示;
        REPLACE(5); //不论A和B的集合状况,B的范围将全部进行显示,如果和A有交集,则将覆盖A的交集范围;
 }  `

clipPath( ),clipRect( ),clipRegion( );通过Path,Rect,Region的不同组合,几乎可以支持任意形状的裁剪区域!

这里演示一个Region.Op.DIFFERENCE,将画笔改为FILL,然后

`   canvas.clipRect(10, 10, 110, 110);        //第一个
    canvas.clipRect(50, 50, 150, 150, Region.Op.DIFFERENCE); //第二个
    canvas.drawRect(0, 0, 200, 200, paint_green);`

画布状态

save():把当前状态的状态进行保存,然后放入栈中

restore():把栈顶画布状态取出

画布变换

画布变换主要包括translate(位移)、scale(缩放)、rotate(旋转)、skew(倾斜),画布操作可以帮助我们更加容易的方式制作图形。

【位移translate】

public void translate(float dx, float dy)

基于当前坐标系的移动,并不是屏幕左上角的原点位置

` //构造一个矩形
    Rect rect1 = new Rect(0, 0, 200, 100);
    canvas.translate(50, 50);
    //在平移画布前用绿色画下边框
    canvas.drawRect(rect1, paint_green);

    //平移画布后,再用红色边框重新画下这个矩形
    canvas.translate(50, 50);
    canvas.drawRect(rect1, paint_red);`

【缩放scale】

public void scale (float sx, float sy),分别是x,y轴的缩放比例

public final void scale (float sx, float sy, float px, float py),x.y轴的缩放比例,px,py表示缩放中心位置

其中当缩放比例为负数时候会根据缩放中心轴进行翻转

套用网上一个经典的例子(画笔要设置粗一点,不然会有部分显示不全)

` canvas.translate(mWidth/2,mHeight/2);

    RectF rectf = new RectF(-300,-300,300,300);
    for (int i=0;i<15;i++){
        canvas.scale(0.8f,0.8f);
        canvas.drawRect(rectf,paint_red);
    }`

【旋转 rotate】

public void rotate (float degrees),旋转角度(顺时针)

public final void rotate (float degrees, float px, float py),旋转角度和控制点

这个在画表盘啥的很常用。同样跟位移一样,旋转度数也是可以叠加的。

【错切 skew】

public void skew (float sx, float sy)

float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值,

float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值.

`Rect rect1 = new Rect(10,10,200,100);

    canvas.drawRect(rect1, paint_green);
    canvas.skew(1,0);//X轴倾斜45度,Y轴不变
    canvas.drawRect(rect1, paint_red);`

绘制文本

普通水平绘制drawText

这个比较简单,但是需要注意绘制text绘制精确位置使用FontMetrics,主要包含四个参数

ascent = ascent线的y坐标 - baseline线的y坐标;

descent = descent线的y坐标 - baseline线的y坐标;

top = top线的y坐标 - baseline线的y坐标;

bottom = bottom线的y坐标 - baseline线的y坐标;

指定每个文字位置

void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)

void drawPosText (String text, float[] pos, Paint paint)

参数说明

char[] text:要绘制的文字数组

int index::第一个要绘制的文字的索引

int count:要绘制的文字的个数,用来算最后一个文字的位置,从第一个绘制的文字开始算起

float[] pos:每个字体的位置,同样两两一组,如{x1,y1,x2,y2,x3,y3……}

`float []pos=new float[]{80,100,
            100,200,
            120,300,
            140,400};
    canvas.drawPosText("1234", pos, paint);`

··

沿路径绘制

void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)

void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)

参数说明:

float hOffset : 与路径起始点的水平偏移距离

float vOffset : 与路径中心的垂直偏移量

` String string = "测试文字偏移的参数";

    Path circlePath = new Path();
    circlePath.addCircle(220, 200, 100, Path.Direction.CCW);
    canvas.drawPath(circlePath, paint_red);//绘制出路径原形

    Path circlePath2 = new Path();
    circlePath2.addCircle(550, 200, 100, Path.Direction.CCW);
    canvas.drawPath(circlePath2, paint_red);//绘制出路径原形

    paint_green.setTextSize(30);

     //hoffset、voffset参数值全部设为0,看原始状态是怎样的
    canvas.drawTextOnPath(string, circlePath, 0, 0, paint_green);
    //第二个路径,改变hoffset、voffset参数值
    canvas.drawTextOnPath(string, circlePath2, 80, 30, paint_green);`

常用的基本介绍差不多了还有诸如

drawBitmapMesh:只对绘制的Bitmap作用,使其变形drawVertices:使得画布变形等等实在撸不动了。

参考文章:

http://blog.csdn.net/harvic880925/article/details/39080931

https://github.com/GcsSloop/AndroidNote/blob/master/CustomView/Advance/[2]Canvas_BasicGraphics.md

http://blog.csdn.net/lonelyroamer/article/details/8349601

http://www.runoob.com/w3cnote/android-tutorial-canvas-api1.html

http://www.cnblogs.com/tianzhijiexian/p/4297664.html

canvas的常见用法的更多相关文章

  1. Linux中find常见用法

    Linux中find常见用法示例 ·find   path   -option   [   -print ]   [ -exec   -ok   command ]   {} \; find命令的参数 ...

  2. php中的curl使用入门教程和常见用法实例

    摘要: [目录] php中的curl使用入门教程和常见用法实例 一.curl的优势 二.curl的简单使用步骤 三.错误处理 四.获取curl请求的具体信息 五.使用curl发送post请求 六.文件 ...

  3. Guava中Predicate的常见用法

    Guava中Predicate的常见用法 1.  Predicate基本用法 guava提供了许多利用Functions和Predicates来操作Collections的工具,一般在 Iterabl ...

  4. find常见用法

    Linux中find常见用法示例 ·find   path   -option   [   -print ]   [ -exec   -ok   command ]   {} \; find命令的参数 ...

  5. iOS 开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  6. iOS开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  7. [转]EasyUI——常见用法总结

    原文链接: EasyUI——常见用法总结 1. 使用 data-options 来初始化属性. data-options是jQuery Easyui 最近两个版本才加上的一个特殊属性.通过这个属性,我 ...

  8. NSString常见用法总结

    //====================NSStirng 的常见用法==================== -(void)testString { //创建格式化字符串:占位符(由一个%加一个字 ...

  9. [转]Linux中find常见用法示例

    Linux中find常见用法示例[转]·find   path   -option   [   -print ]   [ -exec   -ok   command ]   {} \;find命令的参 ...

随机推荐

  1. 使用Myeclipse10.0自动生成搭建SSH框架(数据库表自动反向转换成Hibernate实体)实现用户登陆

    我这里使用的数据库是mysql5.0 数据是上课用的.这些都不是重点,重要的是学会这个方法: 创建好数据库: create database jboadefault character set utf ...

  2. mysql-workbench工具update(更新)失败的解决办法

    是因为安全模式的保护,所以我们需要设置一下: 如下:windows下是edit–>preferences–>SQL Editor 把右边的最后一行,"safe update&qu ...

  3. Android之获取屏幕的尺寸像素及获取状态栏标题栏高度

    在Android的实际开发中,会经常用到获取屏幕的尺寸的问题,以便设置一些布局在屏幕上的固定位置,从而适配各个屏幕的设备. 今天我就来讲一下怎么得到当前设备的屏幕像素吧: 一.在Activity中: ...

  4. 详解EBS接口开发之销售订单导入

     步骤 1. 创建一个订单导入来源.       - 导航到 OM -> 设置 -> 订单 -> 导入来源       - 输入一个新的订单导入来源名称和描述 - 选择启用来激活 ...

  5. 一步步创建Qt Widget项目+TextFinder案例(摘自笔者2015年将出的《QT5权威指南》,本文为试读篇)

     创建一个基于应用的QtWidget应用程序 这个手册描述了怎样使用QtCreater创建个一个小的Qt应用程序,Text Finder.它是Qt工具Text Finder例子的简写版本.这个应用 ...

  6. 【Netty源码学习】EventLoopGroup

    在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...

  7. Linux之mailx的使用

    mailx是UNIX系统上用来处理邮件的工具,使用它可以发送,读取邮件.下面看看如何使用它来发送邮件. 发送格式 mailx -s subject user@xxx.com < message_ ...

  8. Android的ScrollView和HorizontalScrollView-android学习之旅(四十一)

    HorizontalScrollView和ScrollView简介 ScrollView和HorizontalScrollView都继承于FrameLayout组件,两个都是容器,前者为里面的组件添加 ...

  9. (七十九)MapKit的基本使用

    MapKit是苹果公司开发的用于显示地图和实现定位.导航的地图框架. MapKit View可以通过storyboard.xib创建,也可以通过代码直接创建. 需要注意的是,通过storyboard和 ...

  10. Touch Handling in Cocos2D 3.x(四)

    创建触摸生命周期 让我们改善我们的应用程序.如果玩家可以触摸屏幕并且拖放英雄到指定位置不是更好吗? 为了完成这个功能我们必须使用Cocos2d 3.0提供的所有的触摸事件: touchBegan:在用 ...