PathView

android Path的使用

2016-10-14  本文已影响670人  蒸汽飞船

参考:
android绘图之Path总结
Path相关方法讲解(一)
path类的简化结构图


类结构图

内部枚举类

//用于对两个Path对象做相应的运算组合(combine),具体的说是根据不同的op参数及path2参数来影响path1对象,
enum Op:五个枚举值:DIFFERENCE,INTERSECT,UNION,XOR,REVERSE_DIFFERENCE
//填充模式:
enum FillType:四种FillType,WINDING,EVEN_ODD,INVERSE_WINDING,INVERSE_EVEN_ODD 
//往path里添加矩形或圆时候用来指明放心:顺时针或逆时针
enum Direction

构造方法

构造函数有两个,分别是:

/**  
 * Create an empty path  
 */  
public Path() {  
    mNativePath = init1();  
    mDetectSimplePaths = HardwareRenderer.isAvailable();  
}  

/**  
 * Create a new path, copying the contents from the src path.  
 *  
 * @param src The path to copy from when initializing the new path  
 */  
public Path(Path src) {  
    int valNative = 0;  
    if (src != null) {  
        valNative = src.mNativePath;  
    }  
    mNativePath = init2(valNative);  
    mDetectSimplePaths = HardwareRenderer.isAvailable();  
}  

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

常用方法

  1. 基本绘图方法
1.addArc(RectF oval, float startAngle, float sweepAngle)
     绘制弧线,配合Paint的Style可以实现不同的填充效果
2.addCircle(float x, float y, float radius, Path.Direction dir)
     绘制圆形,其中第dir参数用来指定绘制时是顺时针还是逆时针
3.addOval(RectF oval, Path.Direction dir)
     绘制椭圆形,其中 oval作为椭圆的外切矩形区域
4.addRect(RectF rect, Path.Direction dir)
     绘制矩形
5.addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)
     绘制圆角矩形
6.lineTo(float x, float y)
     绘制直线
7.addPath(Path src)
     添加一个新的Path到当前Path
8.arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
     与addArc方法相似,但也有区别,下文细述。
9.quadTo(float x1, float y1, float x2, float y2)
     绘制二次贝塞尔曲线,其中 (x1,y1)为控制点,(x2,y2)为终点
10.cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
     绘制三次贝塞尔曲线,其中(x1,y1),(x2,y2)为控制点,(x3,y3)为终点
  1. rXXX方法
    上面的lineTo,MoveTo,QuadTo,CubicTo方法都有与之对应的rXXX方法:
rLineTo(float dx, float dy)
rMoveTo(float dx, float dy)
rQuadTo(float dx1, float dy1, float dx2, float dy2)
rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

这些方法与之对应的原方法相比,惟一的区别在于:r方法是基于当前绘制开始点的offest,比如当前paint位于 (100,100)处,则使用rLineTo(100,100)
方法绘制出来的直线是从(100,100)到(200,200)的一条直接,由此可见rXXX
方法方便用来基于之前的绘制作连续绘制。

  1. Path.op方法
    此方法用于对两个Path对象做相互运算,类似于:与、或、异或之类
path1.op(path2,Path.Op.XXX);

例子:

Path path1 = new Path();
path1.addCircle(150, 150, 100, Path.Direction.CW);
Path path2 = new Path();
path2.addCircle(200, 200, 100, Path.Direction.CW);
path1.op(path2, Path.Op.UNION);
canvas.drawPath(path1, paint1);

效果如下:

Path.Op.UNION Path.Op.DIFFERENCE
效果:

Path.Op.INTERSECT
效果:
Path.Op.REVERSE_DIFFERENCE
效果:
Path.Op.XOR
效果:
总结:
Path.Op.DIFFERENCE 减去path1中path1与path2都存在的部分;path1 = (path1 - path1 ∩ path2)
Path.Op.INTERSECT 保留path1与path2共同的部分;path1 = path1 ∩ path2
Path.Op.UNION 取path1与path2的并集;path1 = path1 ∪ path2
Path.Op.REVERSE_DIFFERENCE 与DIFFERENCE刚好相反;path1 = path2 - (path1 ∩ path2)
Path.Op.XOR 与INTERSECT刚好相反;path1 = (path1 ∪ path2) - (path1 ∩ path2)
  1. setFillType
    设置path的填充模式.和op类似。android里定义了四种FillType,分别是:
WINDING (0),
EVEN_ODD (1),
INVERSE_WINDING (2),
INVERSE_EVEN_ODD (3)

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


FillType

参考代码:

public class PathFillTypeView extends View {
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Path mPath;

    public PathFillTypeView(Context context) {
        super(context);
        setFocusable(true);
        setFocusableInTouchMode(true);

        mPath = new Path();
        mPath.addCircle(40, 40, 45, Path.Direction.CCW);
        mPath.addCircle(80, 80, 45, Path.Direction.CCW);
        mPath.addCircle(120, 120, 45, Path.Direction.CCW);
    }

    private void showPath(Canvas canvas, int x, int y, Path.FillType ft,
                          Paint paint) {
        canvas.save();
        canvas.translate(x, y);
        canvas.clipRect(0, 0, 160, 160);
        canvas.drawColor(Color.WHITE);
        mPath.setFillType(ft);
        canvas.drawPath(mPath, paint);
        canvas.restore();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = mPaint;
        paint.setColor(Color.RED);
        canvas.drawColor(0xFFCCCCCC);
        canvas.translate(20, 20);
        paint.setAntiAlias(true);
        showPath(canvas, 0, 0, Path.FillType.WINDING, paint);
        showPath(canvas, 160 * 2, 0, Path.FillType.EVEN_ODD, paint);
        showPath(canvas, 0, 160 * 2, Path.FillType.INVERSE_WINDING, paint);
        showPath(canvas, 160 * 2, 160 * 2, Path.FillType.INVERSE_EVEN_ODD, paint);
    }
}
效果如下: FillType

其他方法

close():闭合当前路径 (系统会自动从起点到终点绘制一条直线,使当前路径闭合)
path.reset():清除掉path里的线条和曲线,但是不会改变它的fill-type(后面setFillType再说);
path.rewind():清除掉path里的线条和曲线,但是会保留内部的数据结构以便重用;
path.set(Path src);用src的内容替换原path的内容,和硬件加速有关,开启的话有可能出不来效果。
isInverseFillType():是否是 逆 填充模式:WINDING 和 EVEN_ODD 返回false,INVERSE_WINDING 和 INVERSE_EVEN_ODD 返回true;
toggleInverseFillType():切换相反的填充模式,如WINDING ->INVERSE_WINDING 
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):

关于setPath()方法注意:

path.set(Path src)可能受硬件加速影响
//父View绘制背景时把子view所在的区域过滤掉不绘制(不太可取)
public class ODRelativeLayout extends RelativeLayout {
    public ODRelativeLayout(Context context) {
        super(context);
    }

    public ODRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWillNotDraw(false);
    }

    public ODRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.save();
        Path path = getChildsPath();
        if(path!=null){
            path.moveTo(0,0);
            path.lineTo(getWidth(),getHeight());
            canvas.clipPath(path);
        }
        super.draw(canvas);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.restore();
        super.dispatchDraw(canvas);
    }

    Paint paint = new Paint();
    {
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL);
    }

    private Path getChildsPath() {
        int size = getChildCount();
        if(size==0){
            return null;
        }
        Path path = new Path();
        path.setFillType( Path.FillType.INVERSE_WINDING);
        for (int i = 0; i < size; i++) {
            View cv = getChildAt(i);
            if(cv.isShown()){
                path.addRect(cv.getLeft(),cv.getTop(),cv.getRight(),cv.getBottom(), Path.Direction.CW);
            }
        }
        return path;

    }
}

上一篇下一篇

猜你喜欢

热点阅读