Android Canvas+Paint 画图

一、Path相关讲解

主要讲下android里的Path(封装了贝塞尔曲线)& Canvas里的drawPath(path,paint);

很多人听到贝塞尔曲线,就觉得似乎挺高端大气上档次,后面会和大家一起揭开它的面纱,一睹真容;

Path(路径):

我们先看看Path类里有哪些方法

Android Canvas+Paint 画图

Android Canvas+Paint 画图

咱们从上往下看:

构造函数有两个,分别是

[html] view plain copy
  1. /**  
  2.  * Create an empty path  
  3.  */  
  4. public Path() {  
  5.     mNativePath = init1();  
  6.     mDetectSimplePaths = HardwareRenderer.isAvailable();  
  7. }  

[html] view plain copy
  1. /**  
  2.  * Create a new path, copying the contents from the src path.  
  3.  *  
  4.  * @param src The path to copy from when initializing the new path  
  5.  */  
  6. public Path(Path src) {  
  7.     int valNative = 0;  
  8.     if (src != null) {  
  9.         valNative = src.mNativePath;  
  10.     }  
  11.     mNativePath = init2(valNative);  
  12.     mDetectSimplePaths = HardwareRenderer.isAvailable();  
  13. }  

这没啥好说的,第二种就是直接复用src 里设置的属性创建一个新的Path对象;

path.reset():清除掉path里的线条和曲线,但是不会改变它的fill-type(后面setFillType再说);

path.rewind():清除掉path里的线条和曲线,但是会保留内部的数据结构以便重用;

path.set(Path src);用src的内容替换原path的内容,一起看个小例子:

创建一个path,添加一个实心圆到path里

[html] view plain copy
  1. mEndPath = new Path();  
  2. mEndPath.addCircle(300, 300, 100, Direction.CW);  
绘制该path:

[html] view plain copy
  1. canvas.drawPath(mEndPath, mPaint);  
效果如下,无可厚非:

Android Canvas+Paint 画图

此时在path里再添加一个矩形:

[html] view plain copy
  1. mEndPath = new Path();  
  2. mEndPath.addCircle(300, 300, 100, Direction.CW);  
  3. mEndPath.addRect(new RectF(50, 50, 250, 200), Direction.CW);  
效果如下:

Android Canvas+Paint 画图

做如下改动:

[html] view plain copy
  1. mEndPath = new Path();  
  2. mEndPath.addCircle(300, 300, 100, Direction.CW);  
  3. //mEndPath.addRect(new RectF(50, 50, 250, 200), Direction.CW);  
  4.   
  5. mSrcPath = new Path();  
  6. mSrcPath.addRect(new RectF(50, 50, 250, 200), Direction.CW);  
  7. mEndPath.set(mSrcPath);  
直接运行,如果在4.0以上的机器上(4.0及以上硬件加速默认开启),会发现屏幕上什么都没有了,说明该方法会受到硬件加速的影响,关掉硬件加速,再看效果:

Android Canvas+Paint 画图

下面一起来看看Path 的 FillType - 填充模式:

android里定义了四种FillType,分别是:

 WINDING          (0),

 EVEN_ODD         (1),

 INVERSE_WINDING    (2),

 INVERSE_EVEN_ODD   (3)

有张图可以专门用来说明这四种模式的差别:

Android Canvas+Paint 画图

以上图示已经非常清晰,我们还是用如下代码做下测试:

[html] view plain copy
  1. mEndPath = new Path();  
  2. mEndPath.addCircle(300, 300, 150, Direction.CW);  
  3. mEndPath.addCircle(380, 380, 150, Direction.CW);  
  4. mEndPath.setFillType(FillType.INVERSE_EVEN_ODD);  
  5.   
  6. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  7. mPaint.setStyle(Style.FILL);  
  8. mPaint.setColor(Color.RED);  
测试结果如下图:

Android Canvas+Paint 画图

不设置FillType:

Android Canvas+Paint 画图           Android Canvas+Paint 画图

setFillType(FillType.WINDING)          setFillType(FillType.EVEN_ODD): 


Android Canvas+Paint 画图           Android Canvas+Paint 画图

setFillType(FillType.INVERSE_WINDING):   setFillType(FillType.INVERSE_EVEN_ODD):


根据以上图示,Path的FillType可以总结如下:

1.Path的默认FillType为 FillType.WINDING;

2.作用的范围为绘制 Path 的 Canvas 整体,而非 path 所在区域;

3.FillType.WINDING:取path所有所在区域;

4.FillType.EVEN_ODD:取path所在并不相交区域;

5.FillType.INVERSE_WINDING:取path所有未占区域;

6.FillType.INVERSE_EVEN_ODD:取path未占或相交区域;

下面看看和填充模式相关的几个方法:

getFillType():不用多说,返回 Path 的填充模式;

setFillType():设置 Path 的填充模式;

isInverseFillType():是否是 逆 填充模式:

WINDING 和 EVEN_ODD 返回false,INVERSE_WINDING 和 INVERSE_EVEN_ODD 返回true;

toggleInverseFillType():切换相反的填充模式,举个小例子:

[html] view plain copy
  1. mEndPath = new Path();  
  2. mEndPath.addCircle(300, 300, 150, Direction.CW);  
  3. mEndPath.addCircle(380, 380, 150, Direction.CW);  
  4. mEndPath.setFillType(FillType.WINDING);  
  5. mEndPath.toggleInverseFillType();  
  6.   
  7. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  8. mPaint.setStyle(Style.FILL);  
  9. mPaint.setColor(Color.RED);  
此时给Path设置了WINDING的填充模式,调用toggleInverseFillType(),最终的模式为:

FillType.INVERSE_WINDING

Android Canvas+Paint 画图

isEmpty():path是否为空,如果path不包含任何线条和曲线,则返回true,否则返回false;

isRect(RectF rect):如果path指定的是一个rect,则返回true,否则返回false,如果返回true & rect 不为null,则将该rect设置为path 的区域;

computeBounds(RectF bounds,boolean exact):计算path所在区域,并将结果写入bounds,如果整个path只包含0或1个点,将返回(0,0,0,0):

用如下代码做下测试:

[html] view plain copy
  1. mComputeRect = new RectF();  
  2. mEndPath = new Path();  
  3. mEndPath.addCircle(380, 380, 150, Direction.CW);  
  4. mEndPath.addRect(new RectF(200, 300, 500, 500), Direction.CW);  
  5. mEndPath.computeBounds(mComputeRect, false);  
  6. Toast.makeText(  
  7.         mContext,  
  8.         "" + mComputeRect.left + "," + mComputeRect.top + "," + mComputeRect.right + ","  
  9.                 + mComputeRect.bottom,  
  10.         Toast.LENGTH_LONG).show();  

返回结果为(200,230,530,530),即path所含内容的边界区域

Android Canvas+Paint 画图

incReserve(int extraPtCount):提示path将会增加extraPtCount个点,这能使path有效率的分配它的存储空间;


二、Paint

1、Xfermode:

    

setXfermode 

设置两张图片相交时的模式 

我们知道 在正常的情况下,在已有的图像上绘图将会在其上面添加一层新的形状。 如果新的Paint是完全不透明的,那么它将完全遮挡住下面的Paint; 

而setXfermode就可以来解决这个问题
 


一般来说 用法是这样的 

 

Canvas canvas = new Canvas(bitmap1);

paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));

canvas.drawBitmap(mask, 0f, 0f, paint);      


就是在图片bitmap1上面绘制图片mask时 处理两者相交时候显示的问题 

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



Mode的值 如下图 

Android Canvas+Paint 画图

 

   PorterDuff.Mode.CLEAR 清除画布上图像 
       PorterDuff.Mode.SRC 显示上层图像 
       PorterDuff.Mode.DST 显示下层图像 
       PorterDuff.Mode.SRC_OVER上下层图像都显示,上层居上显示 
       PorterDuff.Mode.DST_OVER 上下层都显示,下层居上显示 
       PorterDuff.Mode.SRC_IN 取两层图像交集部门,只显示上层图像 
       PorterDuff.Mode.DST_IN 取两层图像交集部门,只显示下层图像 
       PorterDuff.Mode.SRC_OUT 取上层图像非交集部门 
       PorterDuff.Mode.DST_OUT 取下层图像非交集部门 
       PorterDuff.Mode.SRC_ATOP 取下层图像非交集部门与上层图像交集部门 
       PorterDuff.Mode.DST_ATOP 取上层图像非交集部门与下层图像交集部门 
       PorterDuff.Mode.XOR 取两层图像的非交集部门

画图示例(画圆角矩形轨迹 常用语市面上流行的poker倒计时):

Android Canvas+Paint 画图
package com.k.gameview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
 
public class GameView1 extends View implements Runnable {
    /* 声明Paint对象 */
    private Paint mPaint = null;

    public GameView1(Context context) {
        super(context);
        /* 构建对象 */
        mPaint = new Paint();

        /* 开启线程 */
        new Thread(this).start();
    }

    
    float arc;
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(arc>360)
        arc=0;
        /* 设置画布的颜色 */
        canvas.drawColor(Color.BLACK);
        
        /* 设置取消锯齿效果 */
        mPaint.setAntiAlias(true);
        mPaint.setAlpha(255);
        if(arc>200){
            
            mPaint.setColor(Color.YELLOW);
        }else{
            
            mPaint.setColor(Color.GREEN);
        }
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        RectF rf = new RectF(10, 10, 90, 140);
        canvas.drawRoundRect(rf, 10, 10, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        mPaint.setAlpha(0);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawArc(new RectF(-100, -75, 200, 225), 240, arc, true, mPaint);
        arc+=2.5;
        
    }

    // 触笔事件
    public boolean onTouchEvent(MotionEvent event) {
        return true;
    }

    // 按键按下事件
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return true;
    }

    // 按键弹起事件
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        return false;
    }

    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
        return true;
    }

    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            // 使用postInvalidate可以直接在线程中更新界面
            postInvalidate();
        }
    }
}
Android Canvas+Paint 画图

Android Canvas+Paint 画图  Android Canvas+Paint 画图