现在的位置: 首页 > 综合 > 正文

android图形图像编程-第三章 路径(Path)与剪裁(Clipping)

2013年09月12日 ⁄ 综合 ⁄ 共 7626字 ⁄ 字号 评论关闭

  1. 路径(Path)与剪裁(Clipping) 路径是封装了多条几何轮廓的组合。其中可以包含线段,圆,椭圆,二次和三次的贝塞尔曲线。路径的主要应用场景有:绘制复杂的轮廓,也可以通过这些路径进行剪裁,或者在路径上显示文字。

3.1 路径(Path)

3.1.1 绘制复杂轮廓

使用路径可以轻易的生成一段复杂的曲线,因为Path类提供了丰富的函数来实现此功能。下面罗列并简要解释下这些函数。

public void addArc(RectF oval, float startAngle, float sweepAngle)。向路径中添加椭圆弧,此椭圆弧的位置由外接矩形oval定义。startAngle定义开始的角度。sweepAngle定义弧线扫描过的角度。

public void addCircle(float x, float y, float radius, Path.Direction dir)。向路径中添加圆。(x,y)定义圆心的位置。raidus定义圆的半径。dir定义了圆加入路径时的方向。方向由枚举类Path.Direction定义。此参数只用于加入路径的封闭曲线中。

Path.Direction.CCW 逆时针方向。

Path.Direction.CW 顺时针方向。

public void addOval(RectF oval, Path.Direction dir)。向路径中添加椭圆。oval定义了椭圆的位置和大小。Path.Direction 定义了椭圆加入路径时的方向。

public void addPath(Path src, float dx, float dy)。将一段路径加入当前路径。(dx,dy)定义了原路径的偏移量。

public void addPath(Path src)。将src所指路径加入当前路径。

public void addPath (Path src, Matrix matrix)。将src所指路径加入当前路径。用matrix所指矩阵进行变换。(变化应用于src还是添加src后的全路径?)

public void addRect(float left, float top, float right, float bottom, Path.Direction dir)。向路径中添加矩形。

public void addRect(RectF rect, Path.Direction dir)。向路径中添加矩形。

public void addRoundRect(RectF rect, float[] radii, Path.Direction dir)。向路径中添加圆角矩形。radii数组定义圆角矩形的四个圆角的x,y半径。radii长度必须为8。

public void addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)。想路径中添加圆角矩形。rx,ry定义了四个圆角的x,y半径。

public void arcTo(RectF oval, float startAngle, float sweepAngle)。向路径中添加一段椭圆弧。

arcTo和addArc的区别在于:

1. 首先,使用addArc可以直接加入一段椭圆弧。而使用arcTo还需要使用moveTo指定当前点的坐标。

2. 对于arcTo来说,如果当前点坐标和欲添加的曲线的起始点不是同一个点的话,还会自动添加一条直线补齐路径。

public void moveTo(float x, float y)。移动当前点到(x,y)。

public void rMoveTo(float dx, float dy)。

public void lineTo(float x, float y)。从当前路径结束点添加一条路径到指定点(x,y)。

public void rLineTo(float dx, float dy)。同LineTo,区别在于LineTo中的(x,y)是对应于坐标原点。而此处(dx,dy)是对应于路径结束点的相对坐标。其他r*函数都类似。

public void quadTo(float x1, float y1, float x2, float y2)。以当前路径结束点为开始点,(x1,y1)为控制点,(x2,y2)为结束点画一条二次贝塞尔曲线。

public void rQuadTo(float dx1, float dy1, float dx2, float dy2)。同quadTo。

public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)。以当前路径结束点为开始点,(x1,y1),(x2,y2)为控制点,(x3,y3)为结束点画一条三次贝塞尔曲线。

public void rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)。同cubicTo。

1
下面是相关示例代码和运行效果截图:

3-1 使用路径绘制复杂轮廓.png

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package com.example.androidgraphicsprogramm;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;

public class PathBasic extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new SampleView(this));
    }

    private static class SampleView extends View {
        private Paint mPaint;
        Path path;

        public SampleView(Context context) {
            super(context);
            setFocusable(true);

            mPaint = new Paint();
            path = new Path();
        }


        @Override protected void onDraw(Canvas canvas) {
            canvas.drawColor(Color.WHITE);        

            drawPath_1(canvas);
            drawPath_2(canvas);
        }

        public void drawPath_1(Canvas canvas) {
            Paint paint = mPaint;
            int xoffset = 100;
            int yoffset = 100;

            paint.setColor(Color.GREEN);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(5);

            canvas.drawPoint(xoffset,yoffset, paint);

            paint.setColor(Color.RED);

            canvas.save();
            canvas.translate(xoffset, yoffset);
            path.reset();
            RectF rect = new RectF();
            rect.left = 0;
            rect.top = 0;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 100;

            path.addArc(rect, 0,180);

            rect.left = 250;
            rect.top = 0;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 100;
            path.addArc(rect, 180, 180);    

            canvas.drawPath(path, paint);           
            canvas.restore();

            yoffset += 200;
            canvas.save();
            canvas.translate(xoffset, yoffset);

            path.reset();

            rect.left = 0;
            rect.top = 0;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 100;

            path.addOval(rect,Path.Direction.CCW);


            rect.left = 150;
            rect.top = 0;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 100;

            path.addRoundRect(rect,10,5,Path.Direction.CCW);
            canvas.drawPath(path, paint);           
            canvas.restore();

            // 
            yoffset += 200;
            canvas.save();
            canvas.translate(xoffset, yoffset);

            path.reset();
            path.setFillType(Path.FillType.EVEN_ODD);
            mPaint.setStyle(Paint.Style.FILL);

            rect.left = 0;
            rect.top = 0;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 100;

            path.addOval(rect,Path.Direction.CCW);

            rect.left = 150;
            rect.top = 0;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 100;

            path.addRoundRect(rect,10,5,Path.Direction.CCW);
            canvas.drawPath(path, paint);           
            canvas.restore();
        }

        public void drawPath_2(Canvas canvas) {
            Paint paint = mPaint;

            int xoffset = 100;
            int yoffset = 700;

            canvas.save();
            canvas.translate(xoffset, yoffset);

            path.reset();
            //path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(5);

            path.moveTo(0, 0);
            path.lineTo(200, 200);

            RectF rect = new RectF();
            rect.left = 200;
            rect.top = 100;
            rect.right = rect.left + 200;
            rect.bottom = rect.top + 200;

            path.arcTo(rect, 180, 180);
            path.rLineTo(200, -200);
            path.rQuadTo(-150, 100, -300, 0);
            canvas.drawPath(path, paint);
        }

    }
}

3.1.2 路径的特效

android 针对path对象提供了一系列的PathEffect类来实现path绘制的特效。想实现path的特殊绘制效果,需要创建一个响应的PathEffect类,然后调用Paint的setPathEffect方法,将PathEffect类的对象作为参数传入,这样在绘制path时就可以根据PathEffect产生响应的效果了。下面详细介绍下相关的PathEffect类。

1. CornerPathEffect(float radius), 圆角效果。将path中的两条线段形成的锐角夹角用圆弧替代。 CornerPathEffect的构造函数的参数指定了圆弧的半径。

注释:只对锐角有效,这是Android官方文档描述的,原文如下:Transforms geometries that are drawn (either STROKE or FILL styles) by replacing any sharp angles between line segments into rounded angles of the specified radius.但是从我的实验结果看,好像钝角也会被处理。

2. DashPathEffect(float[] intervals, float phase)虚线效果。用虚线绘制路径。构造函数中的参数含义分别为:intervals: 定义path中的虚线单元。s数组的长度必须大于等于2,而且必须为偶数。phase 表示绘制path是的偏移量。

例如,针对本文提供的例子:

e[2] = new DashPathEffect(new float[] {10, 5, 5, 5}, phase);

(10,5,5,5)描述了一条由两条线段和两端空白组成的虚线。整条path就由这个虚线组成。

10 表示第一段线段长度为10,5表示第一段线段之后的空白长度为5,第三个5表示第二条线段长度为5,第四个5表示第二段线段之后的空白长度为5. phase表示绘制是的偏移量,如果phase值为5,则表示绘制时会从第一条长度为10的线段的长度为5的地方开始绘制。因此不断改变phase的值就能实现曲线的动画效果。

3. public DiscretePathEffect (float segmentLength, float deviation)。将原路径切成segmentLength长度定义的线段,并且每条线段在原路径上以deviation指定的偏移量(角度?弧度?)进行偏移。

4. public PathDashPathEffect (Path shape, float advance, float phase, PathDashPathEffect.Style style)。定义一个新的形状(路径)并将其用作原始路径的轮廓标记。参数说明如下:

shape: 定义一个新的形状,绘制出来的路径由一个个这样的形状组成。

advance: 定义两个形状之间的距离。

phase: 定义偏移量,同DashPathEffect种的phase参数含义。

style:定义了图像单元的方向,效果见下图:

第一条蓝色的曲线style取值为:PathDashPathEffect.Style.TRANSLATE

第二条蓝色的曲线style取值为:PathDashPathEffect.Style. MORPH

第三条蓝色的曲线style取值为:PathDashPathEffect.Style. ROTATE
3-2 PathDashPathEffect 参数说明.png

3-2 PathDashPathEffect 参数说明

5. public ComposePathEffect (PathEffect outerpe, PathEffect innerpe)。定义了两种效果的复合作用。

6. public SumPathEffect (PathEffect first, PathEffect second)。也定义了两种效果的组合作用。但是和ComposePathEffect有区别。ComposePathEffect 效果是两种效果的组合,如一个圆角和一个虚线,显示的结果是一条圆角虚线的路径。SumPathEffect的效果则是两种效果的简单叠加。就是用第一种效果先画一遍,再用第二种效果画一遍。下面的效果图是一个很好的说明。

上图:
3-3 路径特效类效果图.png

3-3 路径特效类效果图

上代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
package com.example.androidgraphicsprogramm;

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public class PathEffects extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}

private static class SampleView extends View {
private Paint mPaint;
private Path mPath;
private PathEffect[] mEffects;
private int[] mColors;
private float mPhase = 0;

// private static PathEffect makeDash(float phase) {
// return new DashPathEffect(new float[] { 15, 5, 8, 5 }, phase);
// }

private static void makeEffects(PathEffect[] e, float phase) {
e[0] = null; // no effect
e[1] = new CornerPathEffect(10);
e[2] = new DashPathEffect(

【上篇】
【下篇】

抱歉!评论已关闭.