Android的Paint、Canvas和Matrix讲解

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_AND_STROKE,或STROKE 
 * setStrokeCap(Paint.Cap cap); 
   当画笔样式为STROKE或FILL_AND_STROKE时,设置笔刷的图形样式,如圆形样式  Cap.ROUND,或方形样式Cap.SQUARE 
 * setSrokeJoin(Paint.Join join); 
   设置绘制时各图形的结合方式,如平滑效果等 
 * setStrokeWidth(float width); 
   当画笔样式为STROKE或FILL_AND_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为倾斜弧度 
 * setTypeface(Typeface typeface); 
   设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等 
 * setUnderlineText(boolean underlineText); 
   设置带有下划线的文字效果 
 * setStrikeThruText(boolean strikeThruText); 
   设置带有删除线的效果 

Canvas类介绍

当我们调整好画笔之后,现在需要绘制到画布上,这就得用Canvas类了。在android中既然把Canvas当做画布,那么就可以在画布上绘制我们想要的任何东西。除了在画布上绘制之外,还需要设置一些关于画布的属性,比如,画布的颜色、尺寸等。下面来分析Android中Canvas有哪些功能,Canvas提供了如下一些方法:

1. 设置属性

* Canvas(Bitmap bitmap): 以bitmap对象创建一个画布,则将内容都绘制在bitmap上,因此bitmap不得为null。
* Canvas(GL gl): 在绘制3D效果时使用,与OpenGL相关。
* isOpaque(boolean isOpaque):检测是否支持透明。
* setViewport(int left, int top, int right, int bottom, int clipflag):  设置画布中显示窗口。
* drawColor(int color): 设置Canvas的背景颜色。
* setBitmap(Bitmap mBitmap):  设置具体画布,画的内容,保存为一个Bitmap。
* clipRect(float left, float top, float right, float bottom): 设置显示区域,即设置裁剪区。    
* translate(float x, float  y): 平移画布。
* rotate(float degree, float px, float py):  旋转画布 。
* skew(float sx, float sy):  设置偏移量。 
* save(): 将Canvas当前状态保存在堆栈,save之后可以调用Canvas的平移、旋转、错切、剪裁等操作。
* restore(): 恢复为之前堆栈保存的Canvas状态,防止save后对Canvas执行的操作对后续的绘制有影响。restore和save要配对使用,restore可以比save少,但不能比save多,否则会引发error。save和restore之间,往往夹杂的是对Canvas的特殊操作。
* save(int num):将Canvas当前状态保存在堆栈,并予以编号int
* restoreToCount(int num):恢复为之前堆栈保存的编号为int的Canvas状态
* concat(Matrix matrix):画布关联矩阵,画出来的内容按矩阵改变,而不是画布改变。
* Drawable.draw(Canvas canvas):将Drawable画到Canvas中
   注:这种方式画Drawable怎么设置透明度呢?
   ((BitmapDrawable)Drawable).getPaint().setAlpha(mBgAlpha);

2. 画图

   *  canvas.drawPaint(Paint paint)
      将画笔设置的颜色和透明度铺满画布
   * drawRect(RectF rect, Paint paint) 
      绘制矩形,参数一为RectF一个区域 
   * drawRect(float left, float top, float right, float bottom, Paint paint)
    绘制矩形,left:矩形left的x坐标,top:矩形top的y坐标,right:矩形right的x坐标,bottom:矩形bottom的y坐标
   * drawRoundRect(RectF rect, float rx, float ry, Paint paint)
    绘制圆角矩形, rx:x方向的圆角半径,ry:y方向的圆角半径
   * drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)        
   * drawPath(Path path, Paint paint) 
      绘制一个路径,参数一为Path路径对象
   * drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)  
      贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。
   * drawBitmap (Bitmap bitmap, float left, float top, Paint paint)
   * drawLine(float startX, float startY, float stopX, float stopY, Paintpaint)
     画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。 
   * drawPoint(float x, float y, Paint paint) 
     画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。
   * drawText(String text, float x, floaty, Paint paint)  
     渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二文字左侧到x轴距离,参数三文字BaseLine到y轴距离,参数四是Paint对象。
   * drawOval(RectF oval, Paint paint)
    绘制椭圆,参数一是扫描区域,参数二为paint对象
   * drawOval(float left, float top, float right, float bottom, Paint paint)
   * drawCircle(float cx, float cy, float radius,Paint paint)
    绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;
   * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
   画弧,参数一是RectF对象,指定圆弧的外轮廓矩形区域,参数二是起始角(度)在电弧的开始,参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象;

Canvas对象的获取方式有两种:一种我们通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。

@Override   
protected void onDraw(Canvas canvas) {                                                                                                                                   
} 

另一种就是当你想创建一个Canvas对象时使用的方法:

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

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

从上面方法的名字看来我们可以知道Canvas可以绘制的对象有:弧线(arcs)、填充颜色(argb和color)、Bitmap、圆(circle和oval)、点(point)、线(line)、矩形(Rect)、图片(Picture)、圆角矩形(RoundRect)、文本(text)、顶点(Vertices)、路径(path)。下面我们就演示下canvas的一些简单用法:

绘制圆、椭圆

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint=new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.blue)); 
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(200,200,100,paint);
        canvas.drawOval(500, 100, 800, 300, paint);
        //上面代码等同于
        //RectF rel=new RectF(500,100,800,300);
        //canvas.drawOval(rel, paint);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(20);
        canvas.drawCircle(200,500,90,paint);
        canvas.drawOval(500,400,800,600, paint);
        //上面代码等同于
        //RectF rel2=new RectF(500,400,800,600);
        //canvas.drawOval(rel2, paint);
    }   

Android的Paint、Canvas和Matrix讲解_第1张图片

绘制矩形、圆角矩形

   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setAntiAlias(true); 
        paint.setColor(Color.red));
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(100, 100, 300, 300, paint);
        canvas.drawRoundRect(400, 100, 600, 300, 30, 30, paint);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(20);
        canvas.drawRect(100, 400, 300, 600, paint);
        canvas.drawRoundRect(400, 400, 600, 600, 30, 30, paint);
    }

绘制弧形、封闭弧形

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.orange));         
        RectF rel = new RectF(100, 100, 300, 300);
        //实心圆弧
        canvas.drawArc(rel, 0, 270, false, paint);
        //实心圆弧 将圆心包含在内
        RectF rel2 = new RectF(100, 400, 300, 600);
        canvas.drawArc(rel2, 0, 270, true, paint);
        //设置空心Style
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(20);
        RectF rel3 = new RectF(100, 700, 300, 900);
        canvas.drawArc(rel3, 0, 270, false, paint);
        RectF rel4 = new RectF(100, 1000, 300, 1200);
        canvas.drawArc(rel4, 0, 270, true, paint);
    }

Android的Paint、Canvas和Matrix讲解_第2张图片

绘制文字

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.orange));
        paint.setTextSize(100);
        canvas.drawText("jEh", 80, 150, paint);
    }

Android的Paint、Canvas和Matrix讲解_第3张图片
Android的Paint、Canvas和Matrix讲解_第4张图片

绘制图片

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.music);
        canvas.drawBitmap(bitmap, 100, 100, mPaint);
        //上面代码等同于
        //Rect mSrc = new Rect(0, 0, mBitWidth, mBitHeight); 
        //Rect mDest = new Rect(100,100,100+mBitWidth,100+mBitHeight);
        //canvas.drawBitmap(bitmap, mSrc, mDest, mPaint);
    }

Android的Paint、Canvas和Matrix讲解_第5张图片

绘制Path

通过Path这个类,我们可以画出三角形,梯形等多边形。
常用方法:
moveTo();设置地点
lineTo();连接两点
close();连接起点和终点

Path angle = new Path();
angle.moveTo(250, 0);//设置起点
angle.lineTo(0, 500);
angle.lineTo(500, 500);
angle.close();//闭合路径
canvas.drawPath(angle, mPaint);

Canvas位置转换

通过组合这些对象我们可以画出一些简单有趣的界面出来,但是光有这些功能还是不够的,如果我要画一个仪表盘(数字围绕显示在一个圆圈中)呢? 幸好Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过获得它的转换矩阵对象(getMatrix方法) 直接操作它。这些操作就像是虽然你的笔还是原来的地方画,但是画纸旋转或者移动了,所以你画的东西的方位就产生变化。为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。

canvas.translate() - 画布的平移

  @Override  
  protected void onDraw(Canvas canvas) {  
      super.onDraw(canvas); 
      canvas.drawColor(Color.BLUE);  
      canvas.translate(100, 100);
      mPaint.setColor(Color.RED);
      canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  }

Android的Paint、Canvas和Matrix讲解_第6张图片

canvas.scale( ) - 画布的缩放

关于scale,Android 提供了以下两个接口:

/** * Preconcat the current matrix with the specified scale. * @param sx The amount to scale in X * @param sy The amount to scale in Y */  
public native void scale(float sx, float sy);  

/** * Preconcat the current matrix with the specified scale. * @param sx The amount to scale in X * @param sy The amount to scale in Y * @param px The x-coord for the pivot point (unchanged by the scale) * @param py The y-coord for the pivot point (unchanged by the scale) */  
public final void scale(float sx, float sy, float px, float py) {  
    translate(px, py);  
    scale(sx, sy);  
    translate(-px, -py);  
}  
  @Override  
  protected void onDraw(Canvas canvas) {  
      super.onDraw(canvas); 
      canvas.drawColor(Color.BLUE);
      mPaint.setColor(Color.RED);
      canvas.drawRect(new Rect(0, 0, 800, 800), mPaint); 
      // 保存画布状态  
      canvas.save(); 
      canvas.scale(0.5f, 0.5f); 
      mPaint.setColor(Color.YELLOW); 
      canvas.drawRect(new Rect(0, 0, 800, 800), mPaint); 
      // 画布状态回滚  
      canvas.restore(); 
      canvas.scale(0.5f, 0.5f, 400, 400); 
      mPaint.setColor(Color.GREEN); 
      canvas.drawRect(new Rect(0, 0, 800, 800), mPaint); 
  } 

canvas.rotate( ) - 画布的旋转

canvas.rotate( )和canvas.scale()可以类比起来看,它也有两个可以使用的方法:

/** * Preconcat the current matrix with the specified rotation. * @param degrees The amount to rotate, in degrees */  
public native void rotate(float degrees);    
/** * Preconcat the current matrix with the specified rotation. * @param degrees The amount to rotate, in degrees * @param px The x-coord for the pivot point (unchanged by the rotation) * @param py The y-coord for the pivot point (unchanged by the rotation) */  
public final void rotate(float degrees, float px, float py) {  
    translate(px, py);  
    rotate(degrees);  
    translate(-px, -py);  
}  
  @Override  
  protected void onDraw(Canvas canvas) {  
      super.onDraw(canvas); 
      canvas.drawColor(Color.BLUE);
      mPaint.setColor(Color.RED);
      canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
      canvas.save();
      mPaint.setColor(Color.YELLOW); 
      canvas.rotate(45); 
      canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
      canvas.restore();
      mPaint.setColor(Color.GREEN); 
      canvas.rotate(45,400,400);
      canvas.drawRect(new Rect(0, 0, 800, 800), mPaint);
  }

Android的Paint、Canvas和Matrix讲解_第7张图片

canvas.skew( ) - 画布的错切

public native void skew(float sx, float sy);
这个方法只要理解了两个参数即可:
float sx:将画布在x方向上倾斜相应的角度,sx为倾斜角度的tan值;
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值;
注意,这里全是倾斜角度的tan值,比如我们打算在X轴方向上倾斜45度,tan45=1;

  @Override  
  protected void onDraw(Canvas canvas) {  
      super.onDraw(canvas); 
      canvas.drawColor(Color.BLUE); 
      mPaint.setColor(Color.RED);
      canvas.drawRect(new Rect(0, 0, 400, 400), mPaint); 
      canvas.save();
      //x方向上倾斜45度  
      canvas.skew(1, 0); 
      mPaint.setColor(Color.YELLOW); 
      canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);
      canvas.restore();
      //y方向上移动400再倾斜45度
      canvas.translate(0, 400);
      canvas.skew(0, 1); 
      mPaint.setColor(Color.GREEN); 
      canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);
  } 

Android的Paint、Canvas和Matrix讲解_第8张图片

matrix的变换应用到canvas上

  @Override
  protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
      canvas.drawColor(Color.BLUE);  
      mPaint.setColor(Color.RED);
      canvas.drawRect(0, 0, 100, 100, mPaint);
      canvas.save();
      Matrix matrix = new Matrix();
      matrix.setScale(2f, 2f);
      canvas.concat(matrix);
      canvas.drawRect(100, 100, 200, 200, mPaint);
      canvas.restore();
      canvas.drawRect(400, 400, 500, 500, mPaint);
  }

Android的Paint、Canvas和Matrix讲解_第9张图片

Matrix延伸:
我们通过animation来实现view组件的动画效果时候,实际上是改变canvas的matrix, matrix矩阵的作用主要是对每个坐标点(x,y)转换为另外的(x’,y’),必要的时候canvas还会通过clipRect()方法改变它的绘制可见范围,这样不至于做移动的时候看不到view组件。我们看到view的动画效果时,其实它的大小和布局都没有变化,所以会看到比较搞笑的现象,就是一个button通过translate偏离原来位置后,它的touch事件响应还是在原来位置上,而不是所看到的眼前位置。
Canvas的translate(int dx, int dy)方法,其实和通过设置它的matrix的postTranslate(int dx, int dy), preTranslate(int dx, int dy)方法效果是一样的, 唯独set系列的方法和pre, post的不同,它是直接设值,而后者它们是设置matrix的增量。
更进一步,
比如preTranslate, setTranslate, postTranslate这几个方法的调用顺序对坐标变换的影响。抽象的说pre方法是向前”生长”,从队列前面加入,post方法是向后”生长”,从队列后面加入,然后从前到后按顺序执行队列即可。具体拿个例子来说,比如一个matrix调用了下列一系列的方法:
matrix.preScale(0.5f, 1); matrix.preTranslate(10, 0); matrix.postScale(0.7f, 1); matrix.postTranslate(15, 0); 则坐标变换经过的4个变换过程依次是:translate(10, 0) -> scale(0.5f, 1) -> scale(0.7f, 1) -> translate(15, 0), 所以对matrix方法的调用顺序是很重要的,不同的顺序往往会产生不同的变换效果。pre方法的调用顺序和post方法的互不影响,即以下的方法调用和前者在真实坐标变换顺序里是一致的, matrix.postScale(0.7f, 1); matrix.preScale(0.5f, 1); matrix.preTranslate(10, 0); matrix.postTranslate(15, 0);

而matrix的set方法则会对先前的pre和post操作进行刷除,而后再设置它的值,比如下列的方法调用:
matrix.preScale(0.5f, 1); matrix.postTranslate(10, 0); matrix.setScale(1, 0.6f); matrix.postScale(0.7f, 1); matrix.preTranslate(15, 0); 其坐标变换顺序是translate(15, 0) -> scale(1, 0.6f) -> scale(0.7f, 1).

Canvas里scale, translate, rotate, concat方法都是pre方法,如果要进行更多的变换可以先从canvas获得matrix, 变换后再设置回canvas.

你可能感兴趣的:(android)