图形绘制简介

       Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES 1.0。大部分2D使用的api都在android.graphics和android.graphics.drawable包中。他们提供了图形处理相关的Canvas、ColorFilter、Point、RetcF等类,还有一些动画相关的AnimationDrawable、BitmapDrawable、TransitionDrawable等。
      以图形处理来说,我们最常用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这些都是使用Canvas来实现的。
      另外,我们可以获取View中的Canvas对象,在绘制一些内容后调用View.invalidate方法让View重新刷新,然后再绘制一个新的内容,以此多次之后,就实现了2D动画的效果。

画图需要四大基本要素:
1、一个用来保存像素的Bitmap
2、一个或多个画笔Paint
3、需要绘制的内容
4、一个Canvas画布,用来在Bitmap上使用Paint绘制内容

Canvas对象的获取方式

Canvas对象的获取方式有三种:
1、通过重写View.onDraw方法获取Canvas对象。
这种方式根据环境还分为两种:一种是普通View的Canvas,还有一种是SurfaceView的Canvas。
两种的主要是区别就是,可以在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。
前面一种适合处理量比较小,帧率比较低的动画方面的绘图,比如说象棋游戏之类的;而后一种主要用在游戏或高品质动画方面的绘图。
2、直接创建一个Canvas对象:
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);  
Canvas c = new Canvas(bitmap);
上面代码创建了一个尺寸是100*100的Bitmap,使用它作为Canvas操作的对象,这时候的Canvas就是使用创建的方式。
当你使用创建的Canvas在bitmap上执行绘制方法后,你还可以将绘制的结果提交给另外一个Canvas,这样就可以达到两个Canvas协作完成的效果,简化逻辑。
          但是android SDK建议使用View.onDraw参数里提供的Canvas就好,没必要自己创建一个新的Canvas对象。
3、调用SurfaceHolder.lockCanvas()也会返回一个Canvas对象,可以在 surfaceView 或 TextureView中使用。

Canvas位置装换、保存、恢复

       Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过getMatrix获得它的转换矩阵对象,并可以直接操作它。这些操作就像是虽然你的笔还是在原来的地方画,但是画纸(坐标原点)旋转或者移动了,所以你画的东西的方位就产生了变化。
      为了方便使用这些转换操作,Canvas 还提供了保存和回滚属性的方法save和restore。save用来保存Canvas的状态,save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。restore用来恢复Canvas之前保存的状态,防止save后对Canvas执行的操作对后续的绘制有影响。注意:save和restore要配对使用,如果restore调用次数比save多,会引发Error。

小细节
上图,最初始的情况是栈里只有 0 1 2 3 4 ,然后执行 save 方法两次,则 5 和 6 出现在栈中

Canvas可绘制的图形种类

1、绘制背景(填充)
      drawARGB(int a, int r, int g, int b)
      drawColor(int color)
      drawRGB(int r, int g, int b)
      drawColor(int color, PorterDuff.Mode mode)
2、绘制几何图形
     canvas.drawArc (扇形,弓形,弧线区域)
     canvas.drawCircle(圆)
     canvas.drawOval(圆和椭圆)
     canvas.drawLine(线)
     canvas.drawPoint(点)
     canvas.drawRect(矩形)
     canvas.drawRoundRect(圆角矩形)
     canvas.drawVertices(顶点)
     cnavas.drawPath(路径,可用来绘制任意图形)
3、绘制图片
       canvas.drawBitmap (位图)
       canvas.drawPicture (图片)
4、绘制文本
       canvas.drawText
   

Paint画笔工具相关设置

Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色、样式等绘制信息,指定了如何绘制文本和图形。画笔对象有很多设置方法, 大体上可以分为两类:
1、图形相关的设置方法
  • setARGB(int a,int r,int g,int b); 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
  • setAlpha(int a); 设置绘制图形的透明度。
  • setColor(int color); 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
  • setAntiAlias(boolean aa); 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
  • setDither(boolean dither); 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
  • setFilterBitmap(boolean filter); 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示速度,本设置项依赖于dither和xfermode的设置
  • setMaskFilter(MaskFilter maskfilter); 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等
  • setColorFilter(ColorFilter colorfilter); 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
  • setPathEffect(PathEffect effect); 设置绘制路径的效果,如点画线等
  • setShader(Shader shader); 设置图像效果,使用Shader可以绘制出各种渐变效果
  • setShadowLayer(float radius ,float dx,float dy,int color); 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
  • setStyle(Paint.Style style); 设置画笔的样式。FILL:实心,默认;FILL_OR_STROKE:填充并设置描边;STROKE:描边,空心
  • setStrokeCap(Paint.Cap cap); 定义线段断点形状。当画笔样式为STROKE或FILL_OR_STROKE时,设置我们的画笔在【离开】画板时候留下的最后一点图形,如圆形样式 Cap.ROUND(有延长),或方形样式Cap.BUTT(默认,没有延长)与Cap.SQUARE(有延长)
  • setSrokeJoin(Paint.Join join); 设置绘制时各图形的结合方式(图形节点的样式),如平滑效果等
  • setStrokeWidth(float width); 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细
  • setXfermode(Xfermode xfermode); 设置图形重叠时的处理方式,如合并,取交集或并集
2.文本相关的设置方法
  • setFakeBoldText(boolean fakeBoldText); 模拟实现粗体文字,设置在小字体上效果会比较差
  • setSubpixelText(boolean subpixelText); 设置该项为true,将有助于文本在LCD屏幕上的显示效果
  • setTextAlign(Paint.Align align); 设置绘制文字的对齐方向
  • setTextScaleX(float scaleX); 设置绘制文字x轴的缩放比例,可以实现文字拉伸的效果
  • setTextSize(float textSize); 设置绘制文字的字号大小
  • setTextSkewX(float skewX); 设置斜体文字,skewX为x轴方向的倾斜弧度
  • setTypeface(Typeface typeface); 设置字体风格,包括粗体,斜体以及衬线体,非衬线体等
  • setUnderlineText(boolean underlineText); 设置带有下划线的文字效果
  • setStrikeThruText(boolean strikeThruText); 设置带有删除线的效果

代码-绘制各种图形

public class TestPaintView extends View {
    private Context context;
    private Paint paint;
    private RectF rect;//Rect是使用int类型作为数值,RectF是使用float类型作为数值
    private Path path;//主要用于绘制复杂的图形轮廓,比如折线,圆弧以及各种复杂图案
    private Bitmap bitmap;
    private int left, top, right, bottom;

    public TestPaintView(Context context) {
        super(context);
        this.context = context;
        paint = new Paint();
        rect = new RectF(0, 0, 0, 0);//左X、上Y、右X、下Y相应的距离,即左上角、右下角的坐标,系统不会检查数值的有效性
        paint.setStrokeJoin(Paint.Join.ROUND);//设置绘制时图形的结合方式(图形节点的样式)
        paint.setStrokeCap(Paint.Cap.ROUND);//设置画笔在【离开】画板时留下的最后一点的样式
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStrokeWidth(dp2px(0.5f));//设置画笔粗细,单位为像素
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        //背景颜色
        canvas.drawColor(0x33005500);
        //矩形
        initRect(10, 50, 10, 50);
        paint.setStyle(Paint.Style.STROKE);//描边(空心),默认是FILL(实心、填充)
        canvas.drawRect(rect, paint);
        //绘制弧线区域(扇形或弓形)
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(0xff00ff00);
        canvas.drawArc(rect, 0, 90, false, paint);//当为false时是一个不经过【圆心】的弓形,当为true时是一个经过圆心的扇形
        paint.setColor(0xffff0000);
        canvas.drawArc(rect, 90, 150, true, paint); //圆弧所在矩形,起始角度90,旋转角度150,顺时针为正
        //矩形内切圆(或椭圆)
        paint.setStyle(Paint.Style.STROKE);
        initRect(10, 30, 10, 50);
        canvas.drawRect(rect, paint);
        paint.setARGB(255, 0, 0, 255);
        canvas.drawOval(rect, paint);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawArc(rect, 0, 90, false, paint); //椭圆的扇形
        paint.setARGB(128, 255, 0, 255);
        canvas.drawArc(rect.left, rect.top, rect.right, rect.bottom, 90f, 150f, true, paint);

        //圆角矩形
        paint.setStyle(Paint.Style.STROKE);
        initRect(10, 50, 10, 50);
        canvas.drawRoundRect(rect, 20, 20, paint);//矩形,两侧圆角弧度的大小,一般都设为相同
        //画直线
        canvas.drawLine(dp2px(120), dp2px(25), dp2px(120), dp2px(55), paint);// 画一条线(首尾两点的坐标)
        paint.setColor(Color.BLUE);
        float[] points = new float[] { dp2px(120), dp2px(15), dp2px(150), dp2px(15),//画多条线。每条线都需要两个坐标(每两个值组成一个坐标)
                dp2px(130), dp2px(25), dp2px(150), dp2px(55), dp2px(150), dp2px(25) };
        canvas.drawLines(points, paint);//最后两个值不够组成一条线,所以被废弃掉了。
        paint.setColor(Color.BLACK);
        canvas.drawLines(points, 2, 8, paint);//指定跳过前2个数据,取出8个数据绘制直线
        // 画圆  
        canvas.drawCircle(dp2px(190), dp2px(35), dp2px(25), paint);//圆心坐标,半径
        //画点
        paint.setStrokeWidth(dp2px(3));
        canvas.drawPoint(dp2px(190), dp2px(35), paint);//画一个点
        paint.setColor(Color.RED);
        canvas.drawPoints(points, paint);//画多个点,每两个值组成一个坐标
        //画图片,就是贴图  
        canvas.drawBitmap(bitmap, dp2px(220), dp2px(10), paint);//坐标指的是左上角的位置
        //canvas.drawCircle(320 + bitmap.getWidth() / 2, 200 + bitmap.getWidth() / 2, bitmap.getWidth() / 2, paint);
        //*************************************************************************************

        //画路径1,不规则封闭图形
        paint.setStrokeWidth(dp2px(0.5f));
        path = new Path();
        path.moveTo(dp2px(10), dp2px(70));//指定初始轮廓点,若没指定默认从(0,0)点开始
        path.lineTo(dp2px(10), dp2px(100));//从当前轮廓点绘制一条线段到指定轮廓点
        path.lineTo(dp2px(50), dp2px(100));
        path.lineTo(dp2px(50), dp2px(80));
        path.close(); // 回到初始点形成封闭的曲线
        canvas.drawPath(path, paint);
        //画路径2,利用path也可以画各种图形,api使用上和canvas有些许区别
        rect = new RectF(dp2px(60), dp2px(70), dp2px(110), dp2px(120));
        path.addRect(rect, Direction.CW);//利用path画矩形。Diection.CW 顺时针方向,Diection.CCW 逆时针方向
        canvas.drawPath(path, paint);
        paint.setColor(Color.BLUE);//注意,同一path绘制的图形的颜色一定是相同的
        Path path2 = new Path();//如果这里不重新new一个path,则前面用path绘制的图形的颜色也都会变
        path2.addRoundRect(rect, new float[] { 20, 60, 20, 60, 60, 60, 20, 20 }, Direction.CW);//从左上角顺时针开始,四个角的x轴y轴方向的弧度
        canvas.drawPath(path2, paint);
        //绘制文本
        canvas.drawLine(rect.left, rect.centerY(), rect.right, rect.centerY(), paint);
        paint.reset();//重置
        paint.setTextSize(dp2px(12));//单位是px,只在绘制文字时有效
        paint.setTextAlign(Align.CENTER);//绘制的文字以drawText时指定的 float x 水平居中,默认值是Align.LEFT
        canvas.drawText("1efg", rect.centerX(), rect.centerY(), paint);//注意 float y 代表的是 baseline 的值,也即e和f的下边界,而非g的下边界
        paint.setUnderlineText(true);//带下划线
        canvas.drawText("包青天efg", 0, "包青天efg".length(), rect.right + dp2px(30), rect.top + dp2px(12), paint);//(+textSize)可实现和矩形顶部对齐
        canvas.drawText(new char[] { 'J', '!', '。', '.' }, 0, 4, rect.right + dp2px(30), rect.centerY(), paint);
        //在指定路径上绘制文本
        Path path3 = new Path();
        path3.moveTo(rect.right, rect.bottom);
        path3.lineTo(rect.right + dp2px(150), rect.centerY());
        canvas.drawPath(path3, paint);
        paint.setTextAlign(Align.LEFT);
        canvas.drawTextOnPath("123456789", path3, 30, -10, paint);//float hOffset,相对基准线的向右偏移值, float vOffset向下偏移值
    }

    /**
     * 重新设置矩形边界
     * @param leftAdd, 代表新矩形的左边界距离上一个矩形的右边界的距离
     * @param width,代表矩形的宽度
     * @param topAdd, 代表新矩形的上边界的值
     * @param hight,代表矩形的高度
     */
    private void initRect(int leftAdd, int width, int topValue, int hight) {
        left = right + leftAdd;
        right = left + width;
        top = topValue;
        bottom = top + hight;
        rect.left = dp2px(left);
        rect.right = dp2px(right);
        rect.top = dp2px(top);
        rect.bottom = dp2px(bottom);
    }
    private int dp2px(float dpValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}

代码-位置装换、保存、恢复

public class TestCanvasView extends View {
    private static final int SAVE_FLAGS = //Canvas.ALL_SAVE_FLAG //= 0x1F,还原所有,restore everything when restore() is called 
    Canvas.MATRIX_SAVE_FLAG//= 0x01,需要还原Matrix。restore the current matrix when restore() is called 
            | Canvas.CLIP_SAVE_FLAG //= 0x02,需要还原Clip。restore the current clip when restore() is called 
            | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG //=0x04, 图层的 clip 标记。the layer needs to per-pixel alpha 
            | Canvas.FULL_COLOR_LAYER_SAVE_FLAG//= 0x08,图层的 color 标记。the layer needs to 8-bits per color component
            | Canvas.CLIP_TO_LAYER_SAVE_FLAG;// = 0x10,图层的 clip 标记,在saveLayer 和 saveLayerAlpha时Android强烈建议加上他
    private Paint mPaint;
    public TestCanvasView(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        //画一个红色的圆
        mPaint.setColor(Color.RED);
        canvas.drawCircle(100, 100, 100, mPaint);
        canvas.save();//保存Canvas的状态,save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。save()默认保存的是matrix和clip
        //画一个绿色的拉长的圆(即椭圆)
        canvas.scale(0.5f, 1);//x、y轴的缩放比例
        mPaint.setColor(Color.GREEN);
        canvas.drawCircle(100, 100, 100, mPaint);
        //画一个黄色的矩形
        canvas.restore();//restore用来恢复Canvas之前保存的状态,防止save后对Canvas执行的指定操作对后续的绘制有影响
        canvas.translate(200, 0);//把画布平移。此操作对下面新建的那个图层依然是有效的
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, 200, 200, mPaint);
        canvas.save(SAVE_FLAGS);//指定哪些需要还原。只有指定matrix或clip才有效,其余几个参数在saveLayer和saveLayerAlpha方法中才有效

        //画一个蓝色的扭曲的矩形
        canvas.skew(2f, 1);//图形变换唯一规则:将x坐标全部变为*2,将y坐标全部变为*1。至于是否平行啦之类的都不去限制。
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(0, 0, 100, 100, mPaint);
        //画一个带透明度的蓝色的旋转的小矩形
        canvas.restore();//取消扭曲。
        canvas.translate(200, 0);
        canvas.rotate(30, 50, 50);//沿指定点为中心顺时针旋转指定角度。默认是以左上角为中心
        int count = canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, SAVE_FLAGS);//在指定边界新建一个透明度为0x88的图层
        canvas.drawRect(0, 0, 100, 100, mPaint);//绘制时使用的颜色仍是上次的蓝色,而非save之前的颜色。这个图层的透明度对此图形是有影响的
        //画一个绿色的小圆
        canvas.restore();
        canvas.restore();//如果restore调用的次数大于save的调用次数,会出错。
        canvas.restoreToCount(count);
        mPaint.setColor(Color.GREEN);
        canvas.drawCircle(50, 50, 50, mPaint);// 这个圆是在原来的图层上划的, 没有透明度
    }
}

图形绘制 Canvas Paint Path 详解的更多相关文章

  1. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  2. canvas绘图API详解

    canvas绘图API详解 1.context的状态 矩阵变换属性 当前剪辑区域 context的其他状态属性: strokeStyle, fillStyle, globalAlpha, lineWi ...

  3. canvas arcTo()用法详解 – CodePlayer

    canvas arcTo()用法详解 – CodePlayer canvas arcTo()用法详解

  4. Eclipse Java Build Path详解

    Eclipse Java Build Path详解 1.设置"source folder"与"output folder". * source folder:存 ...

  5. Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢?

    Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢? 可视化图表,有相当多种,但常见的也就下面几种,其他比较复杂一点,大都也是基于如下几种进行组合,变换出来的.对于初学者来说,很容易被这官网上 ...

  6. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  7. 第157天:canvas基础知识详解

    目录 一.canvas简介 1.1 什么是canvas?(了解) 1.2 canvas主要应用的领域(了解) 二.canvas绘图基础 2.0 sublime配置canvas插件(推荐) 2.1 Ca ...

  8. canvas arcTo()用法详解

    CanvasRenderingContext2D对象的方法arcTo()的用法. arcTo(x1, y1, x2, y2, radius) arcTo()方法将利用当前端点.端点1(x1,y1)和端 ...

  9. canvas径向渐变详解

    创建径向渐变步骤如下: 1,创建径向渐变对象 createRadialGradient(x0,y0,r0,x1,y1,r1),其中x0,y0,r0分别为起始圆的位置坐标和半径,x1,y1,r1为终止圆 ...

随机推荐

  1. 如何安装Oracle Database 11g数据库

    先选择你适合你的系统版本,32位系统的请选择32位的,64位系统可以使用32位也可以使用64位,建议采用64位的! 适用于 Microsoft Windows(32 位)的 Oracle Databa ...

  2. iOS更改ShareSDK默认的分享功能界面

    ShareSDK的集成这里就不详细介绍了, 官网的都已经够详细了.. 官方的默认分享样式如下: 贴上我的源代码: // 创建分享图片 NSString *imageURLString = @" ...

  3. Linq中字段数据类型转换问题(Linq to entity,LINQ to Entities 不识别方法"System.String ToString()"问题解决)

    1.在工作中碰到这样一个问题: 使用linq时,需要查询两个表,在这两张表中关联字段分别是int,和varchar()也就是string,在linq中对这两个字段进行关联, 如果强制类型转换两个不同类 ...

  4. 利用谷歌 kaptcha 进行验证码生成

    package main.com.smart.controller; import com.google.code.kaptcha.Producer; import main.com.smart.ut ...

  5. Css3执行后显示最后一针

    -webkit-animation-fill-mode: both; animation-fill-mode: both;

  6. 6 个轻量级并且灵活的 PHP CMS 系统

    Anchor CMS Just like its introduction says, Anchor has a very simple UI. The installation takes lite ...

  7. 《python基础教程》笔记之 列表

    list函数 list函数将其他类型的序列转换为列表,如 >>> list("hello world")['h', 'e', 'l', 'l', 'o', ' ' ...

  8. MyEclipse 点击 部署 按钮 无效的解决办法

    问题描述 1 通常情况下,当我们点击MyEclipse任务栏上的部署按钮时,会弹出项目部署框,如下图:   2 但我们有时也会遇到点击部署按钮怎么也弹不出项目部署框的问题. END 解决方法一: 1 ...

  9. Hive是什么!

    Hive是什么! 一直想抽个时间整理下最近的所学,断断续续接触hive也有半个多月了,大体上了解了很多Hive相关的知识.那么,一般对陌生事物的认知都会经历下面几个阶段: 为什么会出现?解决了什么问题 ...

  10. django(二)视图和URL配置

    创建一份视图: 在上一节,使用django-admin.py startproject制作的mysite文件夹中,创建一个叫做views.py的空文件.这个Python模块健柏寒这一章的视图. vie ...