Android 2D画图类Path精炼详解

一、前期基础知识储备

Android作为全球最大的操作系统,其界面UI历经这么多版本的改良,现在已经可以说不输于iOS系统了,今天本节内容就来分析UI界面中很关键的一部分——绘图

Android中实现绘图有两种机制:

①利用Path来绘制2D界面图形

②利用OpenGL ES来绘制3D图形

(PS:利用OPENGL ES完全可以处理2D 但是要更复杂)

我们都知道Canva提供了一系列的drawXXX方法,可以实现很多现实中常见的图形,比如,drawPoint, drawPoints,drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc,那为什么有这些方法之后,我们还要单独学习Path类呢?其实很简单,上面的方法确实对于处理一些常见的图形而言非常便利,但其本身没有延展性,如果需要实现更复杂的图形,比如五角星、心形等这些图形就要依靠Path类来实现了。

再来看一下官方文档中的描述:

The Path class encapsulates compound(multiple contour) geometric paths consisting of straight line segments, quadraticcurves, and cubic curves. It canbe drawn with canvas.drawPath(path, paint), either filled or stroked (based onthe paint's Style), or it can be used for clipping or to draw text on a path.

从文档中,我们可以知道,Path即可以绘制直线也可以绘制曲线(二次贝塞尔曲线、三次贝塞尔曲线)。你能用Canvas中的drawPath来把这条路径画出来(同样支持Paint的不同绘制模式),也可以用于剪裁画布和根据路径绘制文字。

这里给出所有常见常用的Path类的方法,如下表:

方法

作用

备注

moveTo

移动起点

移动下一次操作的起点位置

lineTo

连接直线

连接上一个点到当前点之间的直线

setLastPoint

设置终点

重置最后一个点的位置

close

闭合路劲

从最后一个点连接最初的一个点,形成一个闭合区域

addRect

添加矩形

添加矩形到当前Path

addRoundRect

添加圆角矩形

添加圆角矩形到当前Path

addOval

添加椭圆

添加椭圆到当前Path

addCircle

添加圆

添加圆到当前Path

addPath

添加路径

添加路径到当前Path

addArc

添加圆弧

添加圆弧到当前Path

arcTo

圆弧

绘制圆弧,注意和addArc的区别

isEmpty

是否为空

判定Path是否为空

isRect

是否为矩形

判定Path是否是一个矩形

set

替换路径

用新的路劲替换当前路径的所有内容

offset

偏移路径

对当前的路径进行偏移

quadTo

贝塞尔曲线

二次贝塞尔曲线的方法

cubicTo

贝塞尔曲线

三次贝塞尔曲线的方法

rMoveTo,rlineTo,
rQuadTo,rCubicTo

rXxx方法

不带r的方法是基于原点坐标系(偏移量),带r的基于当前点坐标系(偏移量)

op

布尔操作

对两个Path进行布尔运算(交集,并集)等操作

setFillType

填充模式

设置Path的填充模式

getFillType

填充模式

获取Path的填充

isInverseFillType

是否逆填充

判断是否是逆填充模式

toggleInverseFillType

相反模式

切换相反的填充模式

getFillType

填充模式

获取Path的填充

incReserve

提示方法

提示Path还有多少个点等待加入

computeBounds

计算边界

计算Path的路劲

reset,rewind

重置路劲

清除Path中的内容(reset相当于new Path , rewind 会保留Path的数据结构)

transform

矩阵操作

矩阵变换

二、上代码,具体分析其中的关键性方法

先创建好待会要使用的画笔:

Paint mPaint = new Paint();             // 创建画笔

mPaint.setColor(Color.BLACK);           // 画笔颜色 - 黑色

mPaint.setStyle(Paint.Style.STROKE);    // 填充模式 - 描边

mPaint.setStrokeWidth(10);              // 边框宽度 - 10

(1)moveTo、 setLastPoint、 lineTo 和 close

  lineTo

public void lineTo (float x, float y)

 

这个方法很简单,从字面上我们也可以理解到这个方法是用来画线的,两点确定一条直线,但是方法里面只传入一个参数,这个参数是线的终点的坐标,线的起点坐标要么是默认的坐标原点,要么是使用moveTo方法确定好的坐标起点。

moveTo和 setLastPoint:

// moveTo

public void moveTo (float x, float y)

// setLastPoint

public void setLastPoint (float dx, float dy)

public void lineTo (float x, float y)

两个方法看似传入的参数类似,但实际上是完全不同的东西,第一个方法是用于设置起点坐标的,和前面的lingTo搭配使用,后者是用于重置终点坐标的,即尽管使用过lingTo确认过一次终点坐标,但使用过setLastPoint之后还是会以后者中设置的终点坐标为准。

Close:

public void close ()


Close()方法用于连接当前最后一个点和最初的一个点(如果不重合的话),形成一个封闭的图形。

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心

Path path = new Path();                     // 创建Path

path.lineTo(200, 200);                      // lineTo

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

path.close();                               // close

canvas.drawPath(path, mPaint);              // 绘制Path

 


注意:close的作用是封闭路径,与连接当前最后一个点和第一个点并不等价。如果连接了最后一个点和第一个点仍然无法形成封闭图形,则close什么 也不做。

(2)addRect、addRoundRect、addOval、addCircle、addPath、addArc和arcTo

总共是6个addXXX方法和1个arcTo方法,这里我们分成三组进行分析:

①第一组:图形addRect、addRoundRect、addOval、addCircle

// 第一类(图形)

// 圆形

public void addCircle (float x, float y, float radius, Path.Direction dir)

// 椭圆

public void addOval (RectF oval, Path.Direction dir)

// 矩形

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)

public void addRoundRect (RectF rect, float rx, float ry, Path.Direction dir)

 

这些方法就是添加基本的形状,和使用Canvas的drawXXX方法是一样使用的,只不过在使用Path时多了一个参数Path.Direction dir,这个参数用于指定画图的方向:

CW——顺时针

CCW——逆时针

我们通过指定画图的方向指定绘制点的坐标的顺序,所以这个参数非常重要。

②第二组:路径addPath

// 第二类(Path)

public void addPath (Path src)

public void addPath (Path src, float dx, float dy)

public void addPath (Path src, Matrix matrix)

 

这个相对比较简单,也很容易理解,就是将两个Path合并成为一个

第三个方法是将src添加到当前path之前先使用Matrix进行变换。

第二个方法比第一个方法多出来的两个参数是将src进行了位移之后再添加进当前path中。

canvas.translate(mWidth / 2, mHeight / 2);  // 移动坐标系到屏幕中心

canvas.scale(1,-1);                         // <-- 注意 翻转y坐标轴

Path path = new Path();

Path src = new Path();

path.addRect(-200,-200,200,200, Path.Direction.CW);

src.addCircle(0,0,100, Path.Direction.CW);

path.addPath(src,0,200);

 

mPaint.setColor(Color.BLACK);           // 绘制合并后的路径

canvas.drawPath(path,mPaint);

 
示例:


首先我们新建地方两个Path(矩形和圆形)中心都是坐标原点,我们在将包含圆形的path添加到包含矩形的path之前将其进行移动了一段距离,最终绘制出来的效果就如上面所示。

第三组:圆弧,addArc与arcTo

// 第三类(addArc与arcTo)

// addArc

public void addArc (RectF oval, float startAngle, float sweepAngle)

// arcTo

public void arcTo (RectF oval, float startAngle, float sweepAngle)

public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

 

参数说明:

RectF oval——圆弧的外切矩形。

startAngle——开始角度

sweepAngle——扫过角度(-360<= sweepAngle <360)

forceMoveTo——是否强制使用MoveTo

addArc与arcTo两个方法都是用于在路径中添加圆弧,其区别在于:前者直接将圆弧添加到路径中去,不用管二者的位置关系;而arcTo方法通过布尔型参数forceMoveTo的指定,关联起圆弧和路径的位置关系。

   

(3)isEmpty、 isRect、isConvex、set 和 offset

这五个方法比较简单,前面三个方法用于做出判断,后面的set方法用于重置整条路径,而offset用法用于对路径进行平移。

最后,还有两个关键的曲线相关方法二阶贝塞尔曲线方法quadTo 、三阶贝塞尔曲线方法cubicTo,难度有蛮大,笔者计划单列出来讲解,那么就不在本篇文章中涉及了。

三、上代码,具体实现一些路径特效

如上文所述,Android的Path类是一个非常有用的类,它可以预先在View上将N个点连成一条“路径”,然后调用Canvas的drawPath(path, paint)方法沿着路径绘制图形。我们观察上面的例子就会发现,形状我们已经可以做出来了,之后我们对画出来的路径做特效处理,当然可以Android还为路径绘制提供了PathEffect来定义绘制特效,PathEffect包含了如下子类(每个子类代表一种绘制效果):

CornerPathEffect——圆滑路径线

DiscretePathEffect——杂点路径线

DashPathEffect——杂点间隔路径线

PathDashPathEffec——规则点间隔路径线

ComposePathEffect——特效组合线

实现代码如下,具体参考自《Android群英传》:

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(new MyView(this));  
    }  
  
    class MyView extends View{  
        float phase;  
        PathEffect[] pathEffects = new PathEffect[7];  
        int[] colors;  
        private Paint paint;  
        Path path;  
        public MyView(Context context) {  
            super(context);  
            paint = new Paint();  
            paint.setStyle(Paint.Style.STROKE);  
            paint.setStrokeWidth(4);  
            //创建并初始化Path  
            path = new Path();  
            path.moveTo(0, 0);  
            for(int i=1; i<=40; i++){  
                //生成40个点,随机生成他们的Y坐标,并将他们连城一条Path  
                path.lineTo(i*20, (float)Math.random()*60);  
            }  
            //初始化7个一颜色  
            colors = new int[]{Color.RED, Color.YELLOW, 
            Color.GREEN, Color.BLACK, Color.BLUE, Color.LTGRAY,Color.CYAN};  
        }  
  
        @Override  
        protected void onDraw(Canvas canvas) {  
            //将背景填充成白色  
            canvas.drawColor(Color.WHITE);  
            //---------------下面开始画7种路径效果------------  
            pathEffects[0] = null;  
            pathEffects[1] = new CornerPathEffect(10);  
            pathEffects[2] = new DiscretePathEffect(3.0f, 5.0f);  
            pathEffects[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, phase);  
            Path p = new Path();  
            p.addRect(0, 0, 8, 8, Path.Direction.CCW);  
            pathEffects[4] = new PathDashPathEffect(p, 12, phase, PathDashPathEffect.Style.ROTATE);  
            //初始化ComposePathEffect  
            pathEffects[5] = new ComposePathEffect(pathEffects[2], pathEffects[4]);  
            pathEffects[6] = new SumPathEffect(pathEffects[4], pathEffects[3]);  
            //将画布移动到(8, 8)处开始绘制,后面的操作都以(8,8)作为参照点,默认原点为(0,0)  
            canvas.translate(80, 80);  
            //依次使用7种不同的路径效果、7种不同的颜色来绘制  
            for(int i=0; i<7; i++){  
                paint.setPathEffect(pathEffects[i]);  
                paint.setColor(colors[i]);  
                canvas.drawPath(path, paint);  
                canvas.translate(0, 200);  
            }  
            //改变phase值,形成动画效果  
            phase += 1;  
            invalidate();  
        }  
    }  
}



猜你喜欢

转载自blog.csdn.net/weixin_41101173/article/details/80151965