Path從懵逼到精通——基本操做

什麼是Path?

咱們先看看Android官方文檔給出的定義:javascript

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

這裏大概翻譯就是:
Path類封裝了直線段,二次貝塞爾曲線和三次貝塞爾曲線的幾何路徑。
可使用Canvas中drawPath方法將Path畫出來。Path不只可使用Paint的填充模式和描邊模式,也能夠用畫布裁剪和或者畫文字。git

總而言之,Path就是能夠畫出經過直線或者曲線的各類組合就能夠作出不少很牛X的效果。github

至於Path能作出多牛X的效果?上圖給大家看看:
canvas

怎麼使用Path?

要想用Path作出牛X的效果以前,就須要熟悉它的基本操做,這篇文章主要介紹的是Path的一些基本API,進階的用法將會放在下一篇文章。api

如下是Path的基本操做的方法:

第一類(直線與點的操做):lineTo,moveTo,setLastPoint,close

第二類(基本形狀):

addXxx,arcTo

第三類(設置方法) :

set(),offset(),reset()

第四類(判斷方法) :isConvex(),isEmpty(),isRect(RectF rect)

在說這些方法以前都要作一個畫筆的初始化,代碼以下:數組

private void initPaint() {
  mPaint = new Paint();       // 建立畫筆
  mPaint.setColor(Color.BLACK);  // 畫筆顏色 - 黑色
  mPaint.setStyle(Paint.Style.STROKE);  // 填充模式 - 描邊
  mPaint.setStrokeWidth(10);  
}複製代碼

第一類(直線與點的操做):

1.1 lineTo:

方法預覽:
public void lineTo (float x, float y)複製代碼
有什麼用:

顧名思義,這個方法就是畫一條直線的。肯定一條直線須要兩個點,可是這個方法裏只提供了一個點的座標啊?那另外一個點的座標是什麼呢?這個點其實就是Path對象上次調用的最後一個點的座標,若是在調用lineTo()方法前,並無調用過任何Path的操做,那這個點就默認爲座標原點。app

怎麼用:

畫出直線:oop

Path path = new Path(); //建立Path對象
    path.lineTo(300, 300); //建立一條從原點到座標(300,300)的直線
    canvas.drawPath(path, mPaint);//畫出路徑複製代碼

效果以下:spa

path.lineTo(300, 300);

這個時候我在path.lintTo(300,300),後面再加一句 path.lineTo(100, 200); 看看效果如何?

Path path = new Path(); //建立Path對象
    path.lineTo(300, 300); //建立一條從原點到座標(300,300)的直線
    path.lineTo(100, 200); //建立從(300,300)到(100,200)的一條直線 
    canvas.drawPath(path, mPaint);//畫出路徑複製代碼

效果以下:

path.lineTo(100, 200);

能夠看到第二段線段的是從(300,300)到(100,200)的,那就能夠知道lineTo方法的鏈接的起點是由lineTo方法上一個Path操做決定的。

1.2 moveTo:

方法預覽:
public void moveTo(float x, float y)複製代碼
有什麼用:

這個方法的做用就是將下次畫路徑起點移動到(x,y)

怎麼用:

仍是用上面的代碼:

Path path = new Path(); //建立Path對象
    path.lineTo(300, 300); //建立一條從原點到座標(300,300)的直線
    path.moveTo(0,0);  //將下一次操做路徑的起點座標移到(0,0)
    path.lineTo(100, 200); //建立從(0,0)到(100,200)的一條直線 
    canvas.drawPath(path, mPaint);//畫出路徑複製代碼

效果以下:

path.moveTo(0,0);

能夠看到在path.lineTo(100, 200);以前調用了path.moveTo(0, 0);方法,那就將lineTo的操做起始點移動到(0,0)。

1.3 setLastPoint:

方法預覽:
public void setLastPoint(float dx, float dy)複製代碼
有什麼用:

改變上一次操做路徑的結束座標點

怎麼用:
Path path = new Path(); //建立Path對象
    path.lineTo(300, 300); //建立一條從原點到座標(300,300)的直線
    path.setLastPoint(500,500);  //將上一次的操做路徑的終點移動到(500,500)
    path.lineTo(100, 200); //建立從(500,500)到(100,200)的一條直線 
    canvas.drawPath(path, mPaint);//畫出路徑複製代碼
效果以下:

path.setLastPoint(500,500);

能夠知道在執行lineTo(100, 100)時座標點是(100,100),使用setLastPoint(500, 500)後就變成(500,500),而且也會影響上一次操做路徑的終點。

在這裏咱們就能夠總結:

方法 做用
moveTo 會影響下次操做,不會影響上一次操做
setLastPoint 會影響下次操做,也會影響上一次操做

1.4 close:

方法預覽:
public void close()複製代碼
有什麼用:

封閉當前路徑,若是當前的點不等於路徑的起始點,就會在整個操做的最後的點與起始點之間添加線段。

######怎麼用:

Path path = new Path(); //建立Path對象
    path.lineTo(300, 300); //建立一條從原點到座標(300,300)的直線
    path.lineTo(100, 200); //建立從(100,200)到(100,200)的一條直線 
    path.close(); //封閉路徑
    canvas.drawPath(path, mPaint);//畫出路徑複製代碼
效果以下:

path.close();

能夠看到在執行close方法以後,在(100,200)與(0,0)之間添加了一條直線

第二類(基本形狀):

2.1 addXxx,arcTo

方法預覽:
//矩形 
public void addRect(RectF rect, Direction dir)

public void addRect(float left, float top, float right, float bottom, Direction dir)

//圓形
public void addCircle(float x, float y, float radius, Direction dir)


//圓角矩形
public void addRoundRect(RectF rect, float[] radii, Direction dir)

public void addRoundRect (float left,float top,float right,float bottom,float rx,float ry,Path.Direction dir)

public void addRoundRect (RectF rect,float[] radii,Path.Direction dir)

public void addRoundRect (float left,float top,float right,float bottom,float[] radii,Path.Direction dir)

//橢圓
public void addOval(RectF oval, Direction dir)

public void addOval (float left,float top,float right,float bottom,Path.Direction dir)

//圓弧
public void addArc (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

// 添加Path
public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)複製代碼
2.1.1 addRect(矩形):
方法預覽:
public void addRect(RectF rect, Direction dir)

public void addRect(float left, float top, float right, float bottom, Direction dir)複製代碼
有什麼用:

畫出一個矩形

怎麼用:
Path path = new Path();  //建立Path對象
RectF rect = new RectF(0, 0, 400, 400);
path.addRect(rect,Path.Direction.CW);
//path.addRect(0, 0, 400, 400, Path.Direction.CW);
//這個方法與上一句是一樣的效果
canvas.drawPath(path, mPaint);複製代碼
效果圖:

path.addRect

解釋:

addRect兩個方法當中前面的全部參數其實都是肯定一個矩形,這裏就不說矩形的原理了,如今重點來講一下addRect的最後一個參數:Path.Direction dir。

這個參數是什麼意思呢?這個參數就是肯定當畫這個矩形的時候到底是順時針方向畫呢?仍是用逆時針方向畫。

Path.Direction.CW表明順時針,Path.Direction.CCW表明逆時針。那這個方法究竟從哪一個點開始畫呢?咱們來驗證一下

Path path = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CW);
path.setLastPoint(0, 300);
canvas.drawPath(path, mPaint);複製代碼

咱們在addRect以後增長setLastPoint方法,從新設置最後一個點的座標。

效果以下:

path.addRect(0, 0, 400, 400, Path.Direction.CW);

若是這個時候咱們將矩形的方向換成逆時針方向,看看效果如何:

Path path = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CCW);
path.setLastPoint(300,0);
canvas.drawPath(path, mPaint);複製代碼
效果以下:

path.addRect(0, 0, 400, 400, Path.Direction.CCW);

從以上兩個效果就知道,addRect方向是從左上上角開始算起的。因此順時針和逆時針的方向是會影響到繪製效果的。

2.1.2 addCircle(圓形):
方法預覽:

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

有什麼用:

畫出一個圓形

怎麼用:
Path path = new Path(); 
    path.addCircle(200, 200, 100, Direction.CW); //建立一個圓心座標爲(200,200),半徑爲100的圓
    canvas.drawPath(path, mPaint);複製代碼
效果以下:

path.addCircle(200, 200, 100, Direction.CW);

2.1.3 addRoundRect(圓角矩形):
方法預覽:
public void addRoundRect(RectF rect, float rx, float ry, Direction dir)

public void addRoundRect (float left,float top,float right,float bottom,float rx,float ry,Path.Direction dir)

public void addRoundRect (RectF rect,float[] radii,Path.Direction dir)

public void addRoundRect (float left,float top,float right,float bottom,float[] radii,Path.Direction dir)複製代碼
有什麼用:

畫出一個圓角矩形

怎麼用:

在說這個方法怎麼用以前,要先說一下圓角矩形的構成原理。
請看下面這幅圖:

圓角矩形.jpg

圓角矩形的圓角其實就是一段圓弧,圓弧須要什麼才能肯定它的位置和大小呢?答案就是圓心和半徑,那爲何上面的方法會出現兩個半徑呢?其實這個並非正圓的半徑,而是橢圓的半徑。

Path path = new Path();
    RectF rect = new RectF(100,100,800,500);
    path.addRoundRect(rect, 150, 100, Direction.CW); //建立一個圓角矩形
    canvas.drawPath(path, mPaint);複製代碼
效果以下:

path.addRoundRect

如今咱們看一下,圓角矩形後面的那兩個方法,這兩個方法都有一個參數: float[] radii 。這個參數的意思就是控制圓角的四個角的半徑。
這個數組至少要有8個值,若是少於8個值就會報異常。這8個值分紅4組,每組的第一和第二個值分別表明圓角的x半徑和y半徑。
每組數據也會做用於圓角矩形的不一樣位置,總結以下

值的位置 做用圓角矩形哪一個角
0,1 左上角
2,3 右上角
4,5 右下角
6,7 左下角
2.1.4 addOval(橢圓):
方法預覽:
//橢圓
public void addOval(RectF oval, Direction dir)

public void addOval (float left,float top,float right,float bottom,Path.Direction dir)複製代碼
有什麼用:

畫一個橢圓

怎麼用:

爲了便於觀察,我將橢圓中的參數的矩形用不一樣顏色畫出來。

Path path = new Path();
  RectF rect = new RectF(100,100,800,500);
  mPaint.setColor(Color.GRAY);
  mPaint.setStyle(Style.FILL);
  canvas.drawRect(rect, mPaint);
  mPaint.setColor(Color.BLACK);
  path.addOval(rect, Direction.CW);
  canvas.drawPath(path, mPaint);複製代碼
效果以下:

path.addOval(rect, Direction.CW);

從效果圖就能夠知道,這個就是矩形的內切圓。那若是想用這個方法畫出正圓應該怎麼畫呢?沒錯,就是將這個矩形變成正方形,畫出來的圓就是正圓了。如今驗證一下:

Path path = new Path();
  RectF rect = new RectF(100,100,800,800); //將矩形變成正方形
  mPaint.setColor(Color.GRAY);
  mPaint.setStyle(Style.FILL);
  canvas.drawRect(rect, mPaint);
  mPaint.setColor(Color.BLACK);
  path.addOval(rect, Direction.CW);
  canvas.drawPath(path, mPaint);複製代碼
效果以下:

用addOval畫出正圓

2.1.5 addArc與arcTo(圓弧):
方法預覽:
//圓弧
public void addArc (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)複製代碼

先說一下 startAngle sweepAngle 這兩個參數的意思。

參數 意思
startAngle 開始的角度
sweepAngle 掃過的角度

startAngle 是表明開始的角度,那麼Android中矩形的0°是從哪裏開始呢?其實矩形的0°是在矩形的右邊的中點,按順時針方向逐漸增大。

如圖:

開始角度

sweepAngle 掃過的角度就是從起點角度開始掃過的角度,並非指終點的角度。例如若是你的startAngle是90°,sweepAngle是180°。那麼這個圓弧的終點應該在270°,而不是在180°。

如今驗證一下看看:

Path path = new Path();
  RectF rect = new RectF(300,300,1000,800);
  mPaint.setColor(Color.GRAY);
  mPaint.setStyle(Style.FILL);
  canvas.drawRect(rect, mPaint);
  mPaint.setColor(Color.BLACK);
  path.addArc(rect, 90, 180);
  canvas.drawPath(path, mPaint);複製代碼

效果以下:

path.addArc(rect, 90, 180);

知道了addArc的用法以後,咱們來看一下arcTo這個方法,這個方法也是用來畫圓弧的,可是與addArc有些不一樣,總結以下:

方法 做用
addArc 直接添加一段圓弧
arcTo 添加一段圓弧,若是圓弧的起點與上一次Path操做的終點不同的話,就會在這兩個點連成一條直線

舉個例子:

Path path = new Path();
    RectF rect = new RectF(300,300,1000,800);
    mPaint.setColor(Color.GRAY);
    mPaint.setStyle(Style.FILL);
    canvas.drawRect(rect, mPaint);
    mPaint.setColor(Color.BLACK);
    mPaint.setStyle(Style.STROKE);
    path.lineTo(100, 100); //用path畫一條從(0,0)到(100,100)的直線
    path.arcTo(rect, 90, 180); //用arcTo方法畫一段圓弧
    canvas.drawPath(path, mPaint); //直線終點(100,100)與圓弧起點會連成一條直線複製代碼

效果以下:

path.arcTo

若是你不想這兩個點連線的話,arcTo在一個方法中有forceMoveTo的參數,這個參數若是設爲true就說明將上一次操做的點設爲圓弧的起點,也就是說不會將圓弧的起點與上一次操做的點鏈接起來。若是設爲false就會鏈接。

來驗證一下:

Path path = new Path();
    RectF rect = new RectF(300,300,1000,800);
    mPaint.setColor(Color.GRAY);
    mPaint.setStyle(Style.FILL);
    canvas.drawRect(rect, mPaint);
    mPaint.setColor(Color.BLACK);
    mPaint.setStyle(Style.STROKE);
    path.lineTo(100, 100); //用path畫一條從(0,0)到(100,100)的直線
    path.arcTo(rect, 90, 180,true); //用arcTo方法畫一段圓弧
    canvas.drawPath(path, mPaint); //直線終點(100,100)與圓弧起點不會連成一條直線複製代碼

效果以下:

path.arcTo(rect, 90, 180,true);

2.1.6 addPath(添加Path):
方法預覽:
//添加Path:
    public void addPath (Path src)
    public void addPath (Path src, float dx, float dy)
    public void addPath (Path src, Matrix matrix)複製代碼

######有什麼用:
將兩個Path合併在一塊兒

######怎麼用:
這裏先講addPath的前兩個方法,最後那個方法等寫到Matrix才細講。

Path path = new Path();
  Path src = new Path();
  path.addRect(0, 0, 400, 400, Path.Direction.CW); //寬高爲400的矩形
  src.addCircle(200, 200, 100, Path.Direction.CW); //圓心爲(200,200)半徑爲100的正圓
  path.addPath(src);
  canvas.drawPath(path, mPaint);複製代碼

######效果以下:

path.addPath(src);

addPath的第二個方法的 dx dy 兩個參數是什麼意思呢?
其實它們是表明添加path後的位移值。
例如,上面這個例子,若是我將path.addPath(src);改爲path.addPath(src,200,0);會出現什麼現象呢?這時候src畫的圓的圓心的座標會移動到(400,200)。

讓咱們來驗證一下:

Path path = new Path();
  Path src = new Path();
  path.addRect(0, 0, 400, 400, Path.Direction.CW); //寬高爲400的矩形
  src.addCircle(200, 200, 100, Path.Direction.CW); //圓心爲(200,200)半徑爲100的正圓
  //path.addPath(src);
  path.addPath(src,200,0);
  canvas.drawPath(path, mPaint);複製代碼

效果以下:

path.addPath(src,200,0);

path畫出寬高爲400的矩形,src畫出一個圓心爲(0,0),半徑爲100的圓。path.addPath將src合併到一塊兒,並將src的中心設置爲(200,200)。

第三類(設置方法):

3.1 set()

方法預覽:
public void set(Path src)複製代碼
有什麼用:

將新的path賦值到現有的path

怎麼用:
Path path = new Path();
  Path src = new Path();
  path.addRect(0, 0, 400, 400, Path.Direction.CW);
  src.addCircle(200, 200, 100, Path.Direction.CW);
  path.set(src); // 至關於 path = src;
  canvas.drawPath(path, mPaint);複製代碼

效果以下:

path.set(src);

這個方法就是將path以前的矩形變成圓形。

3.2 offset()

方法預覽:
public void offset (float dx, float dy)
 public void offset (float dx, float dy, Path dst)複製代碼
有什麼用:

將path進行平移

怎麼用:
Path path = new Path();
  path.addRect(0, 0, 400, 400, Path.Direction.CW);
  canvas.drawPath(path, mPaint);
  mPaint.setColor(Color.RED); //將畫筆變成紅色
  path.offset(100,0);  //將path向右平移
  canvas.drawPath(path, mPaint);複製代碼
效果以下:

path.offset(100,0);

offset的第二個方法的第三個參數的意思就是將平移後的path存儲到dst參數中。
若是傳入dst不爲空,將平移後的狀態存儲到dst中,不影響當前path。dst爲空,平移做用當前的path,至關於第一個方法。
如今驗證一下:

Path path = new Path();
  Path dst = new Path();
  path.addRect(0, 0, 400, 400, Direction.CW); //path添加矩形
  dst.addCircle(100,100, 100, Direction.CW); //dst添加圓形
  path.offset(100,0,dst); //將平移後的path存儲到dst
  canvas.drawPath(dst, mPaint);複製代碼

效果以下:

path.offset(100,0,dst);

3.3 reset()

######方法預覽:

public void reset()複製代碼
有什麼用:

這個方法很簡單,就是將path的全部操做都清空掉。

第四類(判斷方法) :

4.1 isConvex()(這個方法在API21以後纔有)

方法預覽:
public boolean isConvex ()複製代碼
有什麼用:

判斷path是否爲凸多邊形,若是是就爲true,反之爲false。

要理解這個方法首先,咱們要知道什麼是凸多邊形。
凸多邊形的概念:
1.每一個內角小於180度
2.任何兩個頂點間的線段位於多邊形的內部或邊界上。

也就是說矩形,三角形,直線都是凸多邊形,可是五角星那種形狀就不是。如今咱們用代碼驗證一下:

代碼以下:

Path path = new Path();
        path.moveTo(600,600);
        path.lineTo(500,700);
        path.lineTo(380,700);
        path.lineTo(500,780);
        path.close();
        Log.e("Path", "===============path.isConvex() " + path.isConvex());
        canvas.drawPath(path,mPaint);複製代碼

效果以下:

path.isConvex()

打印的結果爲:

E Path    : ===============path.isConvex() false複製代碼

由於該圖形並非凸多邊形,因此返回false。

但這裏有個坑,若是我直接使用addRect方法,而後用setLastPoint來將這個矩形變成凹多邊形。

代碼以下:

Path path = new Path();
  RectF rect = new RectF(0,0,400,400);
  path.addRect(rect, Direction.CCW);
  path.setLastPoint(100, 300);
  Log.e("Path", "===============path.isConvex() " + path.isConvex());
  canvas.drawPath(path,mPaint);複製代碼

效果以下:

path.isConvex()

能夠看出圖形是一個凹多邊形,可是打印的信息倒是:

E Path    : ===============path.isConvex() true複製代碼

這個地方我一直也想不明白,爲何會返回true。等我之後有思路了再來解決這個問題吧。

4.2 isEmpty:

######方法預覽:

public boolean isEmpty ()複製代碼

######有什麼用:
判斷path中是否包含內容:

怎麼用:
Path path = new Path();
  Log.e("path.isEmpty()","==============path.isEmpty()1: " + path.isEmpty());

  path.lineTo(100,100);
  Log.e("path.isEmpty()","==============path.isEmpty()2: " + path.isEmpty());複製代碼

Log輸出的結果:

E path.isEmpty(): ==============path.isEmpty()1: true
E path.isEmpty(): ==============path.isEmpty()2: false複製代碼

###4.3 isRect:

######方法預覽:

public boolean isRect (RectF rect)複製代碼
有什麼用:

判斷path是不是一個矩形,若是是一個矩形的話,將矩形的信息存到參數rect中。

怎麼用:
path.lineTo(0,400);
   path.lineTo(400,400);
   path.lineTo(400,0);
   path.lineTo(0,0);

   RectF rect = new RectF();
   boolean b = path.isRect(rect);
   Log.e("Rect","isRect:"+b+"| left:"+rect.left+"| top:"+rect.top+"| right:"+rect.right+"| bottom:"+rect.bottom);複製代碼

Log輸出的結果:

E Rect    : ======isRect:true| left:0.0| top:0.0| right:400.0| bottom:400.0複製代碼

####參考資料:

相關文章
相關標籤/搜索