Android 2D 绘图

来源:互联网 发布:淘宝店铺花钱解冻 编辑:程序博客网 时间:2024/05/16 21:00

综述

关于颜色滤镜的内容,将在另一篇文章记录
文章内容多为转载,感谢相关作者的分享。具体参考文章在文章末尾已一一列出

一、2D 画图

View

只是把Graphic 资源(images,shapes,colors,pre-defined animation等等这些Android已经实现的一些画图操作)放入View体系,由系统 来将这些Graphic画出来。
在这里没有一笔一画地构造出一个图形出来,即并没有自己去定义画图操作,而是将这些内容放入View中,由系统来将这些内容画出来。这种方式只能画静态或者极为简单的2D图画,对于实时性很强的动画,高品质的游戏都是没法实现的

Canvas

这个 Canvas 是一个 2D 的概念,是在 Skia 中定义的。可以把这个 Canvas 理解成系统提供给我们的一块内存区域(但实际上它只是一套画图的 API,真正的内存是下面的 Bitmap)。这种方式下我们能一笔一划或者使用Graphic来画我们所需要的东西了,要画什么要显示什么都由我们自己控制。
Canvas对象的获取方式有两种:一种我们通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。另一种就是当你想创建一个Canvas对象时使用的方法:

Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);   Canvas c =newCanvas(b);

上 面代码创建了一个尺寸是100*100的Bitmap,使用它作为Canvas操作的对象,这时候的Canvas就是使用创建的方式。当你使用创建的 Canvas在bitmap上执行绘制方法后,你还可以将绘制的结果提交给另外一个Canvas,这样就可以达到两个Canvas协作完成的效果,简化逻 辑。

View canvas — 使用普通 View 的 Canvas 画图

(1)定义一个自己的View :class your_view extends View{} ;
(2)重载View的onDraw方法:protected void onDraw(Canvas canvas){} ;
(3)在onDraw方法中定义你自己的画图操作 ;
除此之外:可以定义自己的Btimap:
// 当自己新建 Canvas 时,需要自己创建一个 bitmap 来存储和显示绘图结果
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);// 必须将这个Bitmap放入View的canvas中,画的 图才能显示出来

Surface View Canvas — 使用专门的 SurfaceView 的 Canvas 来画图

(1)定义一个自己的 SurfaceView : class your_surfaceview extends SurfaceView implements SurfaceHolder.Callback() {}
(2)实现 SurfaceHolder.Callback 的3个方法: surfaceCreated()、surfaceChanged() 、surfaceDestroyed() ;
(3)定义自己的专注于画图的线程: class your_thread extends Thread()
(4)重载线程的 run() 函数。一般在 run 中定义画图操作,在 surfaceCreated 中启动这个线程

2D 绘图相关 API

要掌握Android 2D Graphics必须要熟悉这三个包的各种API。

  • 绘图基本要素:
    Canvas
    Paint
    Bitmap,BitmapFactory,BitmapRegionDecoder,ImageFormat,Movie,NinePatch,YuvImage

  • 过渡模式:
    Xfermode,AvoidXfermode,PixelXorXfermode,PorterDuffXfermode
    PorterDuff

  • 过滤:
    1、rgb过滤 ColorFilter ,ColorMatrixFilter,PorterDuffColorFilter,LightingColorFilter,PorterDuffColorFilter
    2、alpha过滤 MaskFilter,BlurMaskFilter,EmbossMaskFilter
    3、DrawFilter,PaintFlagsDrawFilter
    变换:
    Matrix,Camera,ColorMatrix

  • 颜色:
    Color

  • 渐变:
    Shader
    BitmapShader,ComposeShader,LinearGradient,RadialGradient,SweepGradient

  • 路径
    Path:
    PathEffect,ComposePathEffect,CornerPathEffect,DashPathEffect,DiscretePathEffect,PathDashPathEffect,PathMeasure,SumPathEffect

  • Rasterizer,LayerRasterizer

  • Interpolator,

  • Picture

  • PixelFormat

  • Point,PointF,Rect,RectF

  • SurfaceTexture

  • Typeface

  • Region,RegionIterator

  • Drawable系列

  • Shape系列

总结 2D 画图用到的包

android.view //画图是在View中进行的android.view.animation //定义了一些简单的动画效果Tween Animation 和 Frame. Animation 等android.graphics //定义了画图比较通用的API,比如canvas,paint,bitmap等android.graphics.drawable //定义了相应的Drawable(可画的东西),比如说BitmapDrawable,PictureDrawable等android.graphics.drawable.shapes //定义了一些shape

二、3D画图

针对 3D 画图 SDK 上讲得很简单,就是继承一个 View,然后在这个 View 里面获得 Opengl 的句柄进行画图,道理和 2D 一样的,差别就是一个是使用 2D 的 API 画图,一个是使用 3D 的。
因为 3D openGl ES 具有一套本身的运行机制,比如渲染的过程控制等,因此 Android 提供了一个专门的画图类 GLSurfaceView。这个类被放在一个单独的包 android.opengl 里面,其实现了其他 View 所不具备的操作:
(1) 画图是在一个专门的 Surface 上进行, 这个 Surface 可以最后被组合到 android 的 View 体系中;
(2) Opengl 的运行周期可以与 Activity 的生命周期协调
(3) 具有 OpenGL ES 调用过程中的错误跟踪,检查工具,方便了 Opengl 编程过程的 debug
(4) 可以根据 EGL 的配置来选择自己的 buffer 类型,比如 RGB565,depth=16
(5) 通过 render 来画图,而且 render 对 Opengl 的调用是在一个单独的线程中
GLSurfaceView 是 Android 提供的一个非常值得学习的类,它实际上是一个如何在 View 中添加画图线程的例子,如何在 Java 中使用线程的例子,如何添加事件队列的例子。具体的源码在:/framworks/base/opengl/java/android/opengl/GLSurfaceView.java
GLSurface 画 3D 图形的步骤:
(1)选择画图需要的buffer类型即:EGL配置 setEGLConfigChooser();
(2)选择是否需要Debug信息
setDebugFlags(int)
setGLWrapper(GLSurfaceView.GLWrapper)
(3)为 GLSurfaceView 注册一个画图的 renderer
setRenderer(GLSurfaceView.Renderer)
(4) 设置 render mode:持续渲染或根据命令渲染, setRenderMode(int)
(5) Opengl 的运行和 Activity 的生命周期绑定在一起, 也就是说 Activity pause 的时候,opengl 的渲染也必须 pause。除此之外,GLSurfaceView 提供了线程间交互的函数 queueEvent(Runnable),可以用在主线程和 render 线程之间的交互

class MyGLSurfaceView extends GLSurfaceView {    private MyRenderer mMyRenderer;    public void start() {        mMyRenderer = ...;        setRenderer(mMyRenderer);    }    public boolean onKeyDown(int keyCode, KeyEvent event) {        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {            queueEvent(new Runnable() {                 // This method will be called on the rendering                 // thread:                public void run() {                    mMyRenderer.handleDpadCenter();                }});            return true;        }        return super.onKeyDown(keyCode, event);    }}

Canvas Sample 及常用函数

常用方法

  • drawARGB / drawRGB / drawColor 使用单一的颜色填充画布。
 canvas.drawColor(Color.BLUE);

这里写图片描述

  • drawArc 在一个矩形区域的两个角之间绘制一个弧。
// 参数依次为:弧线所使用的矩形区域大小、开始角度、扫过的角度、是否使用中心canvas.drawArc(rect, 0, 90, false, paint); 

这里写图片描述

// 参数依次为:弧线所使用的矩形区域大小、开始角度、扫过的角度、是否使用中心canvas.drawArc(rect, 0, 90, true, paint); 

这里写图片描述

  • drawBitmap 在画布上绘制一个位图。可以通过指定目标大小或者使用一个矩阵来改变目标位图的外观。

  • drawBitmapMesh 使用一个mesh(网)来绘制一个位图,它可以通过移动网中的点来操作目标的外观。

  • drawCircle 以给定的点为圆心,绘制一个指定半径的圆。
canvas.drawCircle(100, 100, 90, paint);

这里写图片描述

  • drawLine(s) 在两个点之间画一条(多条)直线。
 canvas.drawLine(10, 10, 100, 100, paint);

这里写图片描述

闭合区域

Paint paint=new Paint();paint.setColor(Color.RED);  //设置画笔颜色paint.setStyle(Style.STROKE);//填充样式改为描边paint.setStrokeWidth(5);//设置画笔宽度Path path = new Path();path.moveTo(10, 10); //设定起始点path.lineTo(10, 100);//第一条直线的终点,也是第二条直线的起点path.lineTo(300, 100);//画第二条直线path.lineTo(500, 100);//第三条直线path.close();//闭环canvas.drawPath(path, paint);

这里写图片描述

矩形路径

//先创建两个大小一样的路径  //第一个逆向生成  Path CCWRectpath = new Path();  RectF rect1 =  new RectF(50, 50, 240, 200);  CCWRectpath.addRect(rect1, Direction.CCW);  //第二个顺向生成  Path CWRectpath = new Path();  RectF rect2 =  new RectF(290, 50, 480, 200);  CWRectpath.addRect(rect2, Direction.CW);  //先画出这两个路径   canvas.drawPath(CCWRectpath, paint);  canvas.drawPath(CWRectpath, paint);  //依据路径写出文字  String text="风萧萧兮易水寒,壮士一去兮不复返";  paint.setColor(Color.GRAY);  paint.setTextSize(35);  canvas.drawTextOnPath(text, CCWRectpath, 0, 18, paint);//逆时针生成  canvas.drawTextOnPath(text, CWRectpath, 0, 18, paint);//顺时针生成

两个矩形一个是顺时针,一个是逆时针。在绘制上没有区别,主要区别在文字等基于路径的绘制上
绘制其他形状路径时,情况类似,Paht 都有对应的 add 方法
这里写图片描述
* drawOval 以指定的矩形为边界,画一个椭圆。

//定义一个矩形区域RectF oval =newRectF(0,0,200,300);//矩形区域内切椭圆canvas.drawOval(oval, paint);

这里写图片描述

  • drawPaint 使用指定的Paint填充整个Canvas
  • drawPath 绘制指定的Path。Path对象经常用来保存一个对象中基本图形的集合。
Path path =newPath();//定义一条路径path.moveTo(10, 10);//移动到 坐标10,10path.lineTo(50, 60);path.lineTo(200,80);path.lineTo(10, 10);canvas.drawPath(path, paint);

这里写图片描述

  • drawPicture 在指定的矩形中绘制一个Picture对象。
  • drawPosText 绘制指定了每一个字符的偏移量的文本字符串。
//按照既定点 绘制文本内容   canvas.drawPosText("Android777",newfloat[]{           10,10,//第一个字母在坐标10,10           20,20,//第二个字母在坐标20,20           30,30,//....           40,40,           50,50,           60,60,           70,70,           80,80,           90,90,           100,100   }, paint);

这里写图片描述

  • drawRect 绘制一个矩形。
RectF rect =newRectF(50, 50, 200, 200);canvas.drawRect(rect, paint);   

这里写图片描述

  • drawRoundRect 绘制一个圆角矩形。
 RectF rect =newRectF(50, 50, 200, 200);canvas.drawRoundRect(rect,                           30,//x轴的半径                           30,//y轴的半径                           paint);   

这里写图片描述

  • drawText 在Canvas上绘制一个文本串。文本的字体、大小和渲染属性都设置在用来渲染文本的Paint对象中。如:Paint.setTextSize(Size)
  • drawTextOnPath 在一个指定的path上绘制文本。
Path path =newPath();//定义一条路径path.moveTo(10, 10);//移动到 坐标10,10path.lineTo(50, 60);path.lineTo(200,80);path.lineTo(10, 10);canvas.drawTextOnPath("Android777开发者博客", path, 10, 10, paint);  

这里写图片描述

  • drawVertices 绘制一系列三角形面片,通过一系列顶点来指定它们。
  • drawPoint
    单个点
Paint paint=new Paint();  paint.setColor(Color.RED);  //设置画笔颜色      paint.setStyle(Style.FILL);//设置填充样式   paint.setStrokeWidth(15);//设置画笔宽度    canvas.drawPoint(100, 100, paint);
![这里写图片描述](http://img.blog.csdn.net/20151223160420685)

多个点

void drawPoints (float[] pts, Paint paint)void drawPoints (float[] pts, int offset, int count, Paint paint)
Paint paint=new Paint();  paint.setColor(Color.RED);  //设置画笔颜色      paint.setStyle(Style.FILL);//设置填充样式   paint.setStrokeWidth(15);//设置画笔宽度  float []pts={10,10,100,100,200,200,400,400};  canvas.drawPoints(pts, 2, 4, paint);

drawPoints里路过前两个数值,即第一个点横纵坐标,画出后面四个数值代表的点,即第二,第三个点,第四个点没画
这里写图片描述

sample

paint.setAntiAlias(true);paint.setStyle(Style.STROKE);canvas.translate(canvas.getWidth()/2, 200);//将位置移动画纸的坐标点:150,150canvas.drawCircle(0, 0, 100, paint);//画圆圈//使用path绘制路径文字canvas.save();canvas.translate(-75, -75);Path path =newPath();path.addArc(newRectF(0,0,150,150), -180, 180);Paint citePaint =newPaint(paint);citePaint.setTextSize(14);citePaint.setStrokeWidth(1);canvas.drawTextOnPath("http://www.android777.com", path, 28, 0, citePaint);canvas.restore();Paint tmpPaint =newPaint(paint);//小刻度画笔对象tmpPaint.setStrokeWidth(1);float y=100;int count = 60;//总刻度数for(int i=0 ; i <count ; i++){    if(i%5 == 0){        canvas.drawLine(0f, y, 0, y+12f, paint);        canvas.drawText(String.valueOf(i/5+1), -4f, y+25f, tmpPaint);    }else{        canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);    }    canvas.rotate(360/count,0f,0f);//旋转画纸}       //绘制指针       tmpPaint.setColor(Color.GRAY);       tmpPaint.setStrokeWidth(4);       canvas.drawCircle(0, 0, 7, tmpPaint);       tmpPaint.setStyle(Style.FILL);       tmpPaint.setColor(Color.YELLOW);       canvas.drawCircle(0, 0, 5, tmpPaint);       canvas.drawLine(0, 10, 0, -65, paint);   
![这里写图片描述](http://img.blog.csdn.net/20151223152341925)

Canvas 变换

一个Canvas对象有四大基本要素:1、一个用来保存像素的Bitmap2、一个Canvas在Bitmap上进行绘制操作3、绘制的内容4、绘制的画笔PaintCanvas还提供了一系列位置转换的方法:rorate、scale、translate、skew(扭曲)等。这些变换影响的是绘图时相对于承载像素的 bitmap 的相对位置
canvas.drawColor(Color.BLUE);canvas.save();Paint paint = new Paint();paint.setColor(Color.RED);paint.setStyle(Paint.Style.FILL);canvas.translate(20, 20);canvas.drawRect(0, 0, 20, 20, paint);canvas.restore();canvas.save();canvas.translate(60, 0);canvas.scale(0.5f, 1);canvas.drawRect(0, 0, 20, 20, paint);canvas.restore();canvas.drawRect(0, 0, 20, 20, paint);
![这里写图片描述](http://img.blog.csdn.net/20151223231950748)

Canvas的保存和回滚

为了方便一些转换操作,Canvas还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。![这里写图片描述](http://img.blog.csdn.net/20151223232144723)

Canvas Layer

Canvas 在一般的情况下可以看作是一张画布,所有的绘图操作如drawBitmap, drawCircle都发生在这张画布上,这张画板还定义了一些属性比如Matrix,颜色等等。但是如果需要实现一些相对复杂的绘图操作,比如多层动画,地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)。Canvas提供了图层(Layer)支持,缺省情况可以看作是只有一个图层Layer。如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX, Restore 来创建一些中间层,对于这些Layer是按照“栈结构“来管理的:![这里写图片描述](http://img.blog.csdn.net/20151223232320929) 创建一个新的Layer到“栈”中,可以使用saveLayer, savaLayerAlpha, 从“栈”中推出一个Layer,可以使用restore,restoreToCount。但Layer入栈时,后续的DrawXXX操作都发生在这个Layer上,而Layer退栈时,就会把本层绘制的图像“绘制”到上层或是Canvas上,在复制Layer到Canvas上时,可以指定Layer的透明度(Layer),这是在创建Layer时指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)本例Layers 介绍了图层的基本用法:Canvas可以看做是由两个图层(Layer)构成的,为了更好的说明问题,我们将代码稍微修改一下,缺省图层绘制一个红色的圆,在新的图层画一个蓝色的圆,新图层的透明度为0×88。
 // private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG            | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG            | Canvas.CLIP_TO_LAYER_SAVE_FLAG; canvas.drawColor(Color.BLUE); Paint paint = new Paint();canvas.translate(10, 10);paint.setColor(Color.WHITE);canvas.drawCircle(75, 75, 75, paint);canvas.saveLayerAlpha(0, 0, 400, 400, 0x88, LAYER_FLAGS);paint.setColor(Color.RED);canvas.drawCircle(75, 125, 75, paint);canvas.restore();
![这里写图片描述](http://img.blog.csdn.net/20151223232818021)

Canvas 剪裁

裁剪功能由Canvas提供的一系列的clip…方法 和quickReject方法来完成。 前面已经提到,真正提供可绘制区域的是Canvas内部的mutable bitmap。 Canvas更像是一个图层,我们只能在这上面的图层来绘制东西。裁剪Clip,即裁剪Canvas图层,我们绘制的东西,只能在裁剪区域的范围能才能显示出来。canvas.save()和canvas.restore()不仅对matrix有效,同样对clip有类似的效果。剪裁常用参数如下:
public enum Op {      DIFFERENCE(0), //最终区域为region1 与 region2不同的区域      INTERSECT(1),  // 最终区域为region1 与 region2相交的区域      UNION(2),      //最终区域为region1 与 region2组合一起的区域      XOR(3),        //最终区域为region1 与 region2相交之外的区域      REVERSE_DIFFERENCE(4), //最终区域为region2 与 region1不同的区域      REPLACE(5);    //最终区域为为region2的区域   }
Paint paint=new Paint();paint.setColor(Color.RED);paint.setStrokeWidth(2);paint.setStrokeMiter(100);canvas.save();canvas.clipRect(new Rect(100,100,300,300));canvas.drawColor(Color.BLUE);//裁剪区域的rect变为蓝色canvas.drawRect(new Rect(0,0,100,100), paint);//在裁剪的区域之外,不能显示canvas.drawCircle(150,150, 50, paint);//在裁剪区域之内,能显示canvas.restore();canvas.drawCircle(100, 100, 100, paint);
![这里写图片描述](http://img.blog.csdn.net/20151225173932524)clipRect和clipPath的最大区别在于 Canvas 的 matrix 对 clipRegion 没有影响
Paint paint=new Paint();  canvas.scale(0.5f, 0.5f);  canvas.save();  canvas.clipRect(new Rect(100,100,200,200));//裁剪区域实际大小为50*50  canvas.drawColor(Color.RED);  canvas.restore();  canvas.drawRect(new Rect(0,0,100,100), paint);//矩形实际大小为50*50  canvas.clipRegion(new Region(new Rect(300,300,400,400)));//裁剪区域实际大小为100*100  canvas.drawColor(Color.BLACK);  
![这里写图片描述](http://img.blog.csdn.net/20151225174314693)

Sample

package com.example.jori.canvastest;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.Region;import android.view.View;public class CustomView extends View {    private Paint mPaint;    private Path mPath;    public CustomView(Context context) {        super(context);        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setStrokeWidth(6);        mPaint.setTextSize(16);        mPaint.setTextAlign(Paint.Align.RIGHT);        mPath = new Path();    }    private void drawScene(Canvas canvas) {        canvas.clipRect(0, 0, 100, 100);        canvas.drawColor(Color.YELLOW);        mPaint.setColor(Color.RED);        canvas.drawLine(0, 0, 100, 100, mPaint);        mPaint.setColor(Color.GREEN);        canvas.drawCircle(30, 70, 30, mPaint);        mPaint.setColor(Color.BLUE);        canvas.drawText("Hello", 100, 30, mPaint);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawColor(Color.GRAY);        canvas.save();        canvas.translate(10, 10);        drawScene(canvas);        canvas.restore();        canvas.save();        canvas.translate(160, 10);        canvas.clipRect(10, 10, 90, 90);        canvas.clipRect(30, 30, 70, 70, Region.Op.DIFFERENCE);        drawScene(canvas);        canvas.restore();        canvas.save();        canvas.translate(10, 120);        mPath.reset();        canvas.clipPath(mPath);        mPath.addCircle(50, 50, 50, Path.Direction.CCW);        canvas.clipPath(mPath, Region.Op.REPLACE);        drawScene(canvas);        canvas.restore();        canvas.save();        canvas.translate(160, 120);        canvas.clipRect(0, 0, 60, 60);        canvas.clipRect(40, 40, 100, 100, Region.Op.UNION);        drawScene(canvas);        canvas.restore();        canvas.save();        canvas.translate(10, 240);        canvas.clipRect(0, 0, 60, 60);        canvas.clipRect(40, 40, 100, 100, Region.Op.XOR);        drawScene(canvas);        canvas.restore();        canvas.save();        canvas.translate(160, 240);        canvas.clipRect(0, 0, 60, 60);        canvas.clipRect(40, 40, 100, 100, Region.Op.REVERSE_DIFFERENCE);        drawScene(canvas);        canvas.restore();    }}
![这里写图片描述](http://img.blog.csdn.net/20151225232338857)

Region

clipRegion(Region region) 方法已被弃用,原因不知。由于 clipRegion 会忽略 Matrix 变换,所以下面的代码可能不是你想的样子
// 此 View 已设置 Padding 为 16pcanvas.drawColor(Color.BLUE);Region reginLeft = new Region();reginLeft.set(10, 10, 300, 300);reginLeft.op(100, 100, 400, 400, Region.Op.UNION);canvas.clipRegion(reginLeft);canvas.drawColor(Color.RED);
![这里写图片描述](http://img.blog.csdn.net/20151225235127033)

Paint 常用属性

  • setColor()
Paint.setColor(Color.BLUE);
  • setARGB(a, r, g, b)

  • setStrokeWidth() // 线宽

  • setAlpha() // 设置透明度

  • setAntiAlias() // 抗锯齿

  • setStyle() // 填充方式

    Paint.Style.FILL :填充内部,会忽略任何和 stroke 相关的参数
    Paint.Style.FILL_AND_STROKE :填充内部和描边
    Paint.Style.STROKE :仅描边

这里写图片描述

  • setShadowLayer (float radius, float dx, float dy, int color) 添加阴影
paint.setShadowLayer(10, 15, 15, Color.GREEN);//设置阴影

这里写图片描述

文字相关

  • paint.setTextAlign(Align.CENTER)
    设置文字对齐方式,取值:align.CENTER、align.LEFT或align.RIGHT
  • paint.setTextSize(12)
    设置文字大小
  • paint.setFakeBoldText(true)
    设置是否为粗体文字
  • paint.setUnderlineText(true)
    设置下划线
  • paint.setTextSkewX((float) -0.25)
    设置字体水平倾斜度,普通斜体字是-0.25
  • paint.setStrikeThruText(true)
    设置带有删除线效果
  • paint.setTextScaleX(2)
    只会将水平方向拉伸,高度不会变

效果如下所示

这里写图片描述

这里写图片描述

这里写图片描述

第一行,水平未拉伸的字体;第二行,水平拉伸两倍的字体;第三行,水平未拉伸和水平拉伸两部的字体写在一起,可以发现,仅是水平方向拉伸,高度并未改变

字体相关

Typeface是专门用来设置字体样式的,通过paint.setTypeface()来指定。可以指定系统中的字体样式,也可以指定自定义的样式文件中获取。要构建Typeface时,可以指定所用样式的正常体、斜体、粗体等,如果指定样式中,没有相关文字的样式就会用系统默认的样式来显示,一般默认是宋体。

Typeface font = Typeface.create(familyName,Typeface.NORMAL);  paint.setTypeface(font); 
  • Typeface create(String familyName, int style)
    直接通过指定字体名来加载系统中自带的文字样式
  • Typeface create(Typeface family, int style)
    通过其它Typeface变量来构建文字样式
  • Typeface createFromAsset(AssetManager mgr, String path)
    通过从Asset中获取外部字体来显示字体样式
  • Typeface createFromFile(String path)
    直接从路径创建
  • Typeface createFromFile(File path)
    从外部路径来创建字体样式
  • Typeface defaultFromStyle(int style)
    创建默认字体

上面的各个参数都会用到Style变量,Style的枚举值如下:

  • Typeface.NORMAL //正常体
  • Typeface.BOLD //粗体
  • Typeface.ITALIC //斜体
  • Typeface.BOLD_ITALIC //粗斜体

Xfermode

AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。

PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。

要应用转换模式,可以使用setXferMode方法,如下所示:

AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID);    borderPen.setXfermode(avoid);

Porter-Duff 效果图:
这里写图片描述

参数及效果

PorterDuff.Mode 中不同 mode 算法中符号的意义为:

  • Sa 代表source alpha ,即源 alpha 值
  • Da 代表 Destination alpha ,即 目标alpha值
  • Sc 代表 source color ,即源色值
  • Dc 代表 Destination color ,即目标色值

这所有的计算都以像素为单位,在某一种混合模式下,对每一个像素的alpha 和 color 通过对应算法进行运算,得出新的像素值,进行展示

int saveCount = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, mPaint, Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(mBottomBitmap, mBottomSrcRect, mBottomDestRect, mPaint);mPaint.setXfermode(mPorterDuffXfermode);canvas.drawBitmap(mTopBitmap, mTopSrcRect, mTopDestRect, mPaint);mPaint.setXfermode(null);canvas.restoreToCount(saveCount);

下面为原图效果:
这里写图片描述
这里写图片描述

下面所用代码默认绘制效果:
这里写图片描述

  • PorterDuff.Mode.CLEAR
    所绘制不会提交到画布上。清除模式[0,0],即最终所有点的像素的alpha 和color 都为 0,所以画出来的效果只有白色背景
    这里写图片描述
  • PorterDuff.Mode.SRC
    显示上层绘制图片。只保留源图像的 alpha 和 color ,所以绘制出来只有源图
    这里写图片描述
  • PorterDuff.Mode.DST
    显示下层绘制图片。同上类比,只保留目标图像的 alpha 和 color,所以绘制出来的只有目标图
    这里写图片描述
  • PorterDuff.Mode.SRC_OVER
    正常绘制显示,上下层绘制叠盖
    这里写图片描述
  • PorterDuff.Mode.DST_OVER
    上下层都显示。下层居上显示
    这里写图片描述
  • PorterDuff.Mode.SRC_IN
    取两层绘制交集。显示上层。在两者相交的地方绘制源图像,并且绘制的效果会受到目标图像对应地方透明度的影响
    这里写图片描述
  • PorterDuff.Mode.DST_IN
    取两层绘制交集。显示下层。原理类比上面
    这里写图片描述
  • PorterDuff.Mode.SRC_OUT
    取上层绘制非交集部分。在不相交的地方绘制 源图像,效果受两者 alpha 值影响。(对于效果的疑问可以参考源码中对每个像素的计算公式)
    这里写图片描述
  • PorterDuff.Mode.DST_OUT
    取下层绘制非交集部分
    这里写图片描述
  • PorterDuff.Mode.SRC_ATOP
    取下层非交集部分与上层交集部分
    这里写图片描述
  • PorterDuff.Mode.DST_ATOP
    取上层非交集部分与下层交集部分
    这里写图片描述
  • PorterDuff.Mode.XOR
    公式:[ Sa + Da - 2 * Sa * Da, Sc * ( 1 - Da ) + ( 1 - Sa ) * Dc ]
    在不相交的地方按原样绘制源图像和目标图像,相交的地方受到对应alpha和色值影响,按上面公式进行计算,如果都完全不透明则相交处完全不绘制
    这里写图片描述
  • PorterDuff.Mode.DARKEN
    公式:[ Sa + Da - Sa * Da , Sc * ( 1 - Da ) + Dc * ( 1 - Sa ) + min(Sc , Dc) ]
    该模式处理过后,会感觉效果变暗,即进行对应像素的比较,取较暗值,如果色值相同则进行混合;
    从算法上看,alpha值变大,色值上如果都不透明则取较暗值,非完全不透明情况下使用上面算法进行计算,受到源图和目标图对应色值和alpha值影响
    这里写图片描述
  • PorterDuff.Mode.LIGHTEN
    公式:[ Sa + Da - Sa * Da , Sc * ( 1 -Da ) + Dc * ( 1 - Sa ) + max ( Sc , Dc ) ]
    可以和 DARKEN 对比起来看,DARKEN 的目的是变暗,LIGHTEN 的目的则是变亮,如果在均完全不透明的情况下 ,色值取源色值和目标色值中的较大值,否则按上面算法进行计算
    这里写图片描述
  • PorterDuff.Mode.MULTIPLY
    正片叠底,即查看每个通道中的颜色信息,并将基色与混合色复合。结果色总是较暗的颜色。任何颜色与黑色复合产生黑色。任何颜色与白色复合保持不变。当用黑色或白色以外的颜色绘画时,绘画工具绘制的连续描边产生逐渐变暗的颜色。
    这里写图片描述
  • PorterDuff.Mode.SCREEN
    滤色,滤色模式与我们所用的显示屏原理相同,所以也有版本把它翻译成“屏幕”;
    简单的说就是保留两个图层中较白的部分,较暗的部分被遮盖;
    当一层使用了滤色(屏幕)模式时,图层中纯黑的部分变成完全透明,纯白部分完全不透明,其他的颜色根据颜色级别产生半透明的效果
    这里写图片描述

    注:
    canvas原有的图片可以理解为背景,就是dst;新画上去的图片可以理解为前景,就是src。

当有多个绘制时,先绘制的是目标图

参考文章

《android Graphics(一):概述及基本几何图形绘制》
《 Android 2D Graphics学习(一)、android.graphics介绍》
《Android 2D Graphics学习(二)、Canvas篇1、Canvas基本使用》
《Android 2D Graphics学习(二)、Canvas篇2、Canvas裁剪和Region、RegionIterator》
《android之Paint属性介绍》
《Android Paint之 setXfermode PorterDuffXfermode 讲解》

0 0
原创粉丝点击