版權聲明:本文爲博主原創文章,未經博主容許不得轉載html
Github:github.com/AnliaLeejava
你們要是看到有錯誤的地方或者有啥好的建議,歡迎留言評論android
前言:開發過程當中很容易忘記一些API的使用方法,網上搜索或者在源碼裏找也很難短期內篩選出本身須要的,遂本身將這些知識總結一番git
Path類中提供了一套xxxTo方法,其做用是從起點到終點移動path畫筆並繪製線(moveTo方法只移動path畫筆不繪製線),線有直線和曲線。方法彙總以下表所示github
方法名 | 參數解析 |
---|---|
lineTo(float x, float y) | 繪製直線,x:終點x座標值,y:終點y座標值 |
moveTo(float x, float y) | 移動畫筆,x:終點x座標值,y:終點y座標值 |
arcTo(RectF oval, float startAngle, float sweepAngle) | 繪製圓弧,oval:圓弧矩形區域,startAngle:起始角度,sweepAngle:圓弧旋轉的角度 |
arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo) | 繪製圓弧,oval:圓弧矩形區域,startAngle:起始角度,sweepAngle:圓弧旋轉的角度,forceMoveTo:是否在繪製圓弧前移動(moveTo)path畫筆位置 |
arcTo(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean forceMoveTo) | 繪製圓弧,left、top、right、bottom組成圓弧矩形區域,startAngle:起始角度,sweepAngle:圓弧旋轉的角度,forceMoveTo:是否在繪製圓弧前移動(moveTo)path畫筆位置 |
quadTo(float x1, float y1, float x2, float y2) | 繪製二階貝塞爾曲線,控制點座標:(x1,y1),終點座標:(x2,y2) |
cubicTo(float x1, float y1, float x2, float y2,float x3, float y3) | 繪製三階貝塞爾曲線,其中控制點1座標爲 (x1,y1),控制點2座標爲(x2,y2),終點座標爲(x3,y3) |
繪製直線,從當前畫筆位置出發,鏈接終點(x,y),示例以下canvas
示例以下數組
path.lineTo(300,300);
canvas.drawPath(path,paint);
複製代碼
移動畫筆,從當前畫筆位置移動到終點(x,y)數據結構
示例以下post
path.moveTo(100,100);
path.lineTo(300,300);
canvas.drawPath(path,paint);
複製代碼
繪製圓弧,從當前畫筆位置出發,連線到內切矩形區域oval的圓弧的起始角度startAngle位置(X軸正方向爲0°),順時針旋轉繪製圓弧,旋轉度數爲sweepAngle(sweepAngle爲負時則逆時針旋轉)動畫
示例以下
RectF rectF = new RectF(100,100,300,400);
path.arcTo(rectF,0,180);
canvas.drawPath(path,pathPaint);
複製代碼
繪製圓弧,若forceMoveTo爲false,則用法和arcTo(RectF oval, float startAngle, float sweepAngle)同樣,繪製圓弧以前不會移動(moveTo)path畫筆位置。若爲true,先強制調用moveTo移動path畫筆至圓弧起點,再繪製圓弧。ps:若是調用arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)方法以前沒有對path進行任何操做,則forceMoveTo設置true或false效果都和設置true同樣
示例以下,注意對比之間的差別
RectF rectF = new RectF(100,100,300,400);
path.moveTo(100,100);
path.arcTo(rectF,0,180,false);
path.close();
canvas.drawPath(path,pathPaint);
複製代碼
RectF rectF = new RectF(100,100,300,400);
path.moveTo(100,100);
path.arcTo(rectF,0,180,true);
path.close();
canvas.drawPath(path,pathPaint);
複製代碼
RectF rectF = new RectF(100,100,300,400);
path.arcTo(rectF,0,180,false);
path.close();
canvas.drawPath(path,pathPaint);
複製代碼
與arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)用法同樣
從path畫筆當前位置出發,以(x₁,y₁)爲控制點,向終點(x₂,y₂)繪製一條二階貝塞爾曲線
示例以下
path.moveTo(100,100);
path.quadTo(200,0,400,100);
canvas.drawPath(path,pathPaint);
複製代碼
從path畫筆當前位置出發,以(x1,y1)爲控制點1,以(x2,y2)爲控制點2,向終點(x3,y3)繪製一條三階貝塞爾曲線
簡單示例以下
path.moveTo(100,100);
path.cubicTo(200,0,300,90,500,100);
canvas.drawPath(path,pathPaint);
複製代碼
圓形其實也是由四段三階貝塞爾曲線組成,咱們繪製其中兩段看看效果便可,示例以下
path.moveTo(300,200);
path.cubicTo(300,200+100*0.551915024494f,200+100*0.551915024494f,300,200,300);
path.moveTo(200-20,300);
path.cubicTo(200-100*0.551915024494f-20,300,100-20,200+100*0.551915024494f,100-20,200);
canvas.drawPath(path,pathPaint);
複製代碼
rXxxTo方法的r意思是relative,即相對的意思,方法有四個,如上圖所示,其功能與對應的xxxTo方法同樣,區別在於rXxxTo方法在繪製Path時是以當前path畫筆位置爲座標原點,即相對於path畫筆位置進行繪製,而xxxTo方法的座標原點則與當前canvas座標原點一致。例如,咱們使用xxxTo方法
path.moveTo(100,100);
path.lineTo(300,300);
canvas.drawPath(path, pathPaint);
複製代碼
上述代碼是從(100,100)到(300,300)繪製一條直線,那麼若是用rXxxTo方法,相對(100,100)這個點繪製直線,則終點應爲(300-100,300-100),即終點設爲(200,200),以下所示
path.moveTo(100,100);
path.rLineTo(200,200);
canvas.drawPath(path, pathPaint);
複製代碼
效果都是同樣的
Path類中還提供了一套addXxx方法,字面理解就是添加一段相應的線,線能夠是曲線、完整的圓形、矩形等,甚至能夠是另外一組Path的線。所謂添加的意思,我我的理解就是在繪製這段線前,移動(moveTo)path畫筆位置到線的起始位置,而後再繪製線,也就是說添加的這段線,與以前繪製的Path是分離的(除非後繪製的這段線的起始點與以前Path的終點一致)。方法彙總以下表所示
方法名 | 參數解析 |
---|---|
addArc(RectF oval, float startAngle, float sweepAngle) | 添加圓弧,oval:圓弧矩形區域,startAngle:起始角度,sweepAngle:圓弧旋轉的角度 |
addArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle) | 添加圓弧,left、top、right、bottom組成圓弧矩形區域,startAngle:起始角度,sweepAngle:圓弧旋轉的角度。ps:此方法在API 19以上有效 |
addCircle(float x, float y, float radius, Direction dir) | 添加圓形,x:圓形圓心的x座標,y:圓形圓心的y座標,radius:圓形半徑,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addOval(RectF oval, Direction dir) | 添加橢圓,oval:橢圓內切的矩形區域,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addOval(float left, float top, float right, float bottom, Direction dir) | 添加橢圓,left、top、right、bottom組成橢圓內切的矩形區域,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addRect(RectF rect, Direction dir) | 添加矩形,rect:矩形區域,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addRect(float left, float top, float right, float bottom, Direction dir) | 添加矩形,left、top、right、bottom組成矩形區域,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addRoundRect(RectF rect, float rx, float ry, Direction dir) | 添加統一圓角的圓角矩形,rect:矩形區域,rx:橢圓圓角的橫軸半徑,ry:橢圓圓角的縱軸半徑,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addRoundRect(float left, float top, float right, float bottom, float rx, float ry,Direction dir) | 添加統一圓角的圓角矩形,left、top、right、bottom組成矩形區域,rx:橢圓圓角的橫軸半徑,ry:橢圓圓角的縱軸半徑,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addRoundRect(RectF rect, float[] radii, Direction dir) | 添加非統一圓角的圓角矩形,rect:矩形區域,radii:矩形四個橢圓圓角的橫軸半徑和縱軸半徑的數組,一共8個數值,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addRoundRect(float left, float top, float right, float bottom, float[] radii,Direction dir) | 添加非統一圓角的圓角矩形,left、top、right、bottom組成矩形區域,radii:矩形四個橢圓圓角的橫軸半徑和縱軸半徑的數組,一共8個數值,dir:線的閉合方向(CW順時針方向 | CCW逆時針方向) |
addPath(Path src) | 添加一組Path,src:要添加的Path |
addPath(Path src, float dx, float dy) | 添加一組平移後的Path,src:要添加的Path,dx:平移的x座標,dy:平移的y座標 |
addPath(Path src, Matrix matrix) | 添加一組通過矩陣變換後的Path,src:要添加的Path,matrix:3x3的矩陣 |
addArc兩個方法使用起來與arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)中forceMoveTo設置爲true效果一致,就不展開贅述了
以點(x,y)爲圓心,添加一個半徑長爲radius的圓形,繪製起始角度爲0°(x軸方向),繪製方向經過dir的值而定,dir爲CW時順時針繪製,dir爲CCW時逆時針繪製
方法比較簡單,主要是對比CW和CCW的區別,咱們用canvas.drawTextOnPath方法突顯順時針和逆時針繪製的效果,示例以下
path.addCircle(200,150,100, Path.Direction.CW);//順時針繪製
canvas.drawPath(path,pathPaint);
canvas.drawTextOnPath("繪製順序", path, 0, 0, paint);
複製代碼
path.addCircle(200,150,100, Path.Direction.CCW);//逆時針繪製
canvas.drawPath(path,pathPaint);
canvas.drawTextOnPath("繪製順序", path, 0, 0, paint);
複製代碼
在oval矩形區域中,添加一個內切的橢圓,繪製起始角度爲0°(x軸方向),繪製方向經過dir的值而定,dir爲CW時順時針繪製,dir爲CCW時逆時針繪製
addOval(RectF oval, Direction dir)和addOval(float left, float top, float right, float bottom, Direction dir)效果是同樣的,就不分開講了
RectF rectF = new RectF(100,100,400,250);
path.addOval(rectF, Path.Direction.CW);
canvas.drawPath(path,pathPaint);
複製代碼
效果如圖
添加一個區域爲rect的矩形,繪製起點爲左上角,繪製方向經過dir的值而定,dir爲CW時順時針繪製,dir爲CCW時逆時針繪製
addRect(RectF rect, Direction dir)和addRect(float left, float top, float right, float bottom, Direction dir)效果是同樣的,就不分開講了
RectF rectF = new RectF(100,100,400,250);
path.addRect(rectF, Path.Direction.CW);
canvas.drawPath(path,pathPaint);
canvas.drawTextOnPath("繪製順序", path, 0, 0, paint);
複製代碼
效果如圖
添加一個區域爲rect的圓角矩形,四個角的圓角大小一致,圓角的橫軸半徑爲rx,縱軸半徑爲ry,dir爲CW時順時針繪製,繪製起點爲左下角,dir爲CCW時逆時針繪製,繪製起點爲左上角(注意對比順時針和逆時針的繪製起點)
addRoundRect(RectF rect, float rx, float ry, Direction dir)和addRoundRect(float left, float top, float right, float bottom, float rx, float ry,Direction dir)效果是同樣的,就不分開講了
RectF rectF = new RectF(100,100,400,350);
path.addRoundRect(rectF,60,30,Path.Direction.CW);//順時針
canvas.drawPath(path,pathPaint);
canvas.drawTextOnPath("繪製順序", path, 0, 0, paint);
複製代碼
RectF rectF = new RectF(100,100,400,350);
path.addRoundRect(rectF,60,30,Path.Direction.CCW);//逆時針
canvas.drawPath(path,pathPaint);
canvas.drawTextOnPath("繪製順序", path, 0, 0, paint);
複製代碼
添加一個區域爲rect的圓角矩形,四個角的圓角的橫軸和縱軸半徑由radii數組中的8個數值決定,dir爲CW時順時針繪製,繪製起點爲左下角,dir爲CCW時逆時針繪製,繪製起點爲左上角(注意對比順時針和逆時針的繪製起點)
須要注意的是,若是radii數組中的元素小於8,系統會拋出錯誤信息radii[] needs 8 values,以下圖所示
addRoundRect(RectF rect, float[] radii, Direction dir)和addRoundRect(float left, float top, float right, float bottom, float[] radii,Direction dir)效果是同樣的,就不分開講了
RectF rectF = new RectF(100,100,400,350);
float[] radii = {60,30,30,70,100,100,10,40};
path.addRoundRect(rectF,radii,Path.Direction.CW);
canvas.drawPath(path,pathPaint);
canvas.drawTextOnPath("繪製順序", path, 0, 0, paint);
複製代碼
添加一組名爲src的Path副本
Path copyPath = new Path();
copyPath.moveTo(100,100);
copyPath.lineTo(150,200);
copyPath.quadTo(200,100,350,200);
copyPath.lineTo(100,250);
copyPath.close();
path.addPath(copyPath);
canvas.drawPath(path,pathPaint);
複製代碼
添加一組名爲src的Path副本,而後將其進行平移,x軸上的平移距離爲dx,y軸上的平移距離爲dy
Path copyPath = new Path();
copyPath.moveTo(100,100);
copyPath.lineTo(150,200);
copyPath.quadTo(200,100,350,200);
copyPath.lineTo(100,250);
copyPath.close();
path.addPath(copyPath,300,0);//向x軸正方向平移300像素距離
canvas.drawPath(path,pathPaint);
複製代碼
添加一組名爲src的Path副本,而後將其進行矩陣變換,矩陣爲matrix(3x3的矩陣)
Path copyPath = new Path();
copyPath.moveTo(100,100);
copyPath.lineTo(150,200);
copyPath.quadTo(200,100,350,200);
copyPath.lineTo(100,250);
copyPath.close();
Matrix mMatrix = new Matrix();
mMatrix.setScale(1,-1);//以x軸爲中線進行翻轉
mMatrix.postRotate(90);//以座標軸原點爲中心點順時針旋轉90°
path.addPath(copyPath,mMatrix);
canvas.drawPath(path,pathPaint);
複製代碼
方法名 | 參數解析 |
---|---|
setFillType(FillType ft) | 設置Path的填充模式,ft:填充類型,有EVEN_ODD ,INVERSE_EVEN_ODD ,WINDING ,INVERSE_WINDING 四種模式 |
getFillType() | 獲取當前Path的填充模式 |
isInverseFillType() | 判斷當前Path填充模式是不是反向規則(INVERSE_XXX) |
toggleInverseFillType() | 當前Path的填充模式與其反向規則模式進行相互切換 |
填充模式要解釋起來仍是挺費口舌的,這裏就把前輩們的博客貼出來,他們解釋得都很是清楚,我就很少贅述了
方法名 | 參數解析 |
---|---|
close() | 封閉當前Path,鏈接起點和終點 |
reset() | 清空Path中的全部直線和曲線,保留填充模式設置,不保留Path上相關的數據結構 |
rewind() | 清空Path中的全部直線和曲線,不保留填充模式設置,但會保留Path上相關的數據結構,以便高效地複用 |
set(Path src) | 用名爲src的Path替換當前的Path |
op(Path path, Op op) | 當前Path與名爲path的Path進行布爾運算(取差集、交集、並集等),op:運算邏輯,有DIFFERENCE(差集),REVERSE_DIFFERENCE(差集),INTERSECT(交集),UNION(並集),XOR(異或)五種運算邏輯可選。ps:此方法在API 19以上有效 |
offset(float dx, float dy) | 平移當前Path,x軸上平移的距離爲dx,y軸上平移的距離爲dy |
offset(float dx, float dy, Path dst) | 平移名爲dst的Path,x軸上平移的距離爲dx,y軸上平移的距離爲dy |
transform(Matrix matrix) | 對當前Path進行矩陣變換,矩陣爲matrix(3x3矩陣) |
transform(Matrix matrix, Path dst) | 對名爲dst的Path進行矩陣變換,矩陣爲matrix(3x3矩陣) |
setLastPoint(float dx, float dy) | 設置終點,設置當前Path最後一個點的位置爲(dx,dy) |
isEmpty() | 判斷當前Path是否爲空 |
isConvex() | 判斷當前Path圍成的圖形是否凸多邊形。ps:此方法在API 21以上有效 |
isRect(RectF rect) | 判斷當前Path是否爲矩形,如是,則將當前Path存儲到新建的rect中 |
這裏大多數方法都比較簡單,有些以前已經應用過,就不展開來說了,下面介紹一下其中比較特別且經常使用的幾個方法
前面的表格咱們提到參數op共有五種運算邏輯可選,下面咱們就來看看這五種運算邏輯是如何影響兩個Path之間的關係的,咱們先用不一樣的顏色繪製出一個矩形和一個圓形,觀察一下它們的位置和關係
Path path1 = new Path();
path1.addRect(100,100,300,300, Path.Direction.CW);
pathPaint.setColor(Color.GREEN);
canvas.drawPath(path1,pathPaint);
Path path2 = new Path();
path2.addCircle(300,250,100,Path.Direction.CW);
pathPaint.setColor(Color.RED);
canvas.drawPath(path2,pathPaint);
複製代碼
下面咱們對這兩個Path進行布爾運算
若op方法的調用關係爲path1.op(path2, Path.Op.DIFFERENCE),則運算結果是path1減去與path2的交集後剩下的部分,即path1與path2的並集減去path2部分
Path path1 = new Path();
path1.addRect(100,100,300,300, Path.Direction.CW);
Path path2 = new Path();
path2.addCircle(300,250,100,Path.Direction.CW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path1.op(path2, Path.Op.DIFFERENCE);//path1與path2進行布爾運算,結果保存至path1
canvas.drawPath(path1,pathPaint);
}
//也能夠這樣寫
Path path3 = new Path();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path3.op(path1,path2,Path.Op.DIFFERENCE);//path1與path2進行布爾運算,結果保存至path3
canvas.drawPath(path3,pathPaint);
}
複製代碼
能夠用path1.op直接運算,也能夠新建一個path3保存path1和path2的運算結果,效果都是同樣的
若op方法的調用關係爲path1.op(path2, Path.Op.REVERSE_DIFFERENCE),則運算結果是path2減去與path1的交集後剩下的部分,即path1與path2的並集減去path1部分
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path1.op(path2, Path.Op.REVERSE_DIFFERENCE);//path1與path2進行布爾運算,結果保存至path1
canvas.drawPath(path1,pathPaint);
}
//也能夠這樣寫
Path path3 = new Path();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path3.op(path1,path2,Path.Op.REVERSE_DIFFERENCE);//path1與path2進行布爾運算,結果保存至path3
canvas.drawPath(path3,pathPaint);
}
複製代碼
若op方法的調用關係爲path1.op(path2, Path.Op.INTERSECT),則運算結果是path1與path2的交集
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path1.op(path2, Path.Op.INTERSECT);//path1與path2進行布爾運算,結果保存至path1
canvas.drawPath(path1,pathPaint);
}
//也能夠這樣寫
Path path3 = new Path();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path3.op(path1,path2,Path.Op.INTERSECT);//path1與path2進行布爾運算,結果保存至path3
canvas.drawPath(path3,pathPaint);
}
複製代碼
若op方法的調用關係爲path1.op(path2, Path.Op.UNION),則運算結果是path1與path2的並集
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path1.op(path2, Path.Op.UNION);//path1與path2進行布爾運算,結果保存至path1
canvas.drawPath(path1,pathPaint);
}
//也能夠這樣寫
Path path3 = new Path();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path3.op(path1,path2,Path.Op.UNION);//path1與path2進行布爾運算,結果保存至path3
canvas.drawPath(path3,pathPaint);
}
複製代碼
若op方法的調用關係爲path1.op(path2, Path.Op.XOR),則運算結果是path1與path2的並集減去path1與path2的交集
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path1.op(path2, Path.Op.XOR);//path1與path2進行布爾運算,結果保存至path1
canvas.drawPath(path1,pathPaint);
}
//也能夠這樣寫
Path path3 = new Path();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
path3.op(path1,path2,Path.Op.XOR);//path1與path2進行布爾運算,結果保存至path3
canvas.drawPath(path3,pathPaint);
}
複製代碼
當Path在調用setLastPoint方法以前執行了某項操做時(繪製直線或曲線等),會將該操做的終點強制設置爲(dx,dy)並連線(線的曲直取決於該操做自己是繪製直線仍是曲線)
理解這個方法以前,首先咱們要知道不管是使用addXxx方法仍是xxxTo方法等在繪製過程當中其實都是根據一堆點的集合,按順序連線(直線或曲線)後繪製出Path最終的樣子,setLastPoint方法正是改變此方法調用以前點的集合中最後一個點的位置。下面咱們經過**封閉圖形(矩形)和非封閉圖形(一段圓弧)**的例子更好地理解這個方法
//用綠線繪製一個矩形
path.addRect(new RectF(100,100,300,300), Path.Direction.CW);
pathPaint.setColor(Color.GREEN);
canvas.drawPath(path,pathPaint);
//強制設置最後一個點爲(150,250),用紅線繪製觀察變化
path.reset();
path.addRect(new RectF(100,100,300,300), Path.Direction.CW);
path.setLastPoint(150,250);
pathPaint.setColor(Color.RED);
canvas.drawPath(path,pathPaint);
複製代碼
//用綠線繪製一個旋轉180°的圓弧
path.addArc(new RectF(100,100,300,300),0,180);
pathPaint.setColor(Color.GREEN);
canvas.drawPath(path,pathPaint);
//強制設置最後一個點爲(200,200),用紅線繪製觀察變化
path.reset();
path.addArc(new RectF(100,100,300,300),0,180);
path.setLastPoint(200,200);
pathPaint.setColor(Color.RED);
canvas.drawPath(path,pathPaint);
複製代碼