本文是我在閱讀了網絡上其他作者的優秀內容之后做的摘錄轉載,其中有對內容的補充。
原來地址:http://www.idtkm.com/customview/customview4/
涉及知識
Path
類型 | API | 描述 |
---|---|---|
添加路徑 | addArc, addCircle, addOval, addPath, addRect, addRoundRect, arcTo | 依次為添加圓弧、圓、橢圓、路徑、矩形、圓角矩形、圓弧 |
移動起點 | moveTo | 移動起點位置,僅對之后路徑產生影響 |
移動終點 | setLastPoint | 移動上一次的終點位置,對前后的路徑都會產生影響 |
直線 | lineTo | 增加一條道指定點的直線 |
貝塞爾 | quadTo, cubicTo | 二階、三階貝塞爾曲線 |
閉合路徑 | close | 路徑終點連接到起點 |
邏輯運算 | op | A\B(DIFFERENCE), A∩B(INTERSECT), B\A(REVERSE_DIFFERENCE), A∪B(UNION), A⊕B(XOR) |
替換路徑 | set | 用新的路徑替換當前路徑 |
重置 | reset, rewind | 清除path使它為空,清除path但保留內部的數據結構 |
計算邊界 | computeBounds | 計算路徑的矩形邊界 |
閉合方向 | Direction | 順時針方向閉合Path(CW),逆時針方向閉合Path(CCW) |
本來這章應該是PieChart的實戰,可是我在編寫的時候發現了一個設置背景圖片的bug。作為一個強迫癥(? _ ?),我只好引入了Path來解決這個bug,所以就有了這一篇內容。
一、什么是Path
官方描述: Path class 封裝了由直線、二次、三次貝塞爾曲線構成的多重曲線幾何路徑。它可以用canvas.drawPath(path,paint)方法繪圖,填充和線都可以(根據paint的樣式),或者它可以用于在繪圖路徑上裁剪或者繪出文本。
我的理解: Path由任意多條直線、二次貝塞爾或三次貝塞爾曲線組成,可以選擇填充或者描邊模式,可以使用它裁剪畫布或者繪制文字。
二、添加路徑
1、lineTo,moveTo
在之前的文章中,使用canvas的函數繪制過[坐標系]
(http://www.idtkm.com/customview/customview2/),這次使用path來繪制。
- a、創建畫筆
創建畫筆并初始化
//創建畫筆
private Paint mPaint = new Paint();
private void initPaint(){ //初始化畫筆
mPaint.setStyle(Paint.Style.FILL);//設置畫筆類型
mPaint.setAntiAlias(true);//抗鋸齒}
- b、繪制坐標軸
使用onSizeChanged方法,獲取根據父布局等因素確認的View寬高
//寬高private int mWidth;
private int mHeight;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
把原點從左上角移動到畫布中心,繪制原點與四個端點
private Path mPath = new Path();
canvas.translate(mWidth/2,mHeight/2);// 將畫布坐標原點移動到中心位置
//繪制坐標原點
mPaint.setColor(Color.BLACK);//設置畫筆顏色
mPaint.setStrokeWidth(10);//為了看得清楚,設置了較大的畫筆寬度
canvas.drawPoint(0,0,mPaint);//繪制坐標軸4個斷點
canvas.drawPoints(new float[]{
mWidth/2*0.8f,0 ,0,mHeight/2*0.8f ,-mWidth/2*0.8f,0 ,0,-mHeight/2*0.8f},mPaint);
增加坐標軸與箭頭的Path,在完成后使用canvas.drawPath一次進行繪制
mPaint.setStrokeWidth(1);//恢復畫筆默認寬度
//x軸mPath.moveTo(-mWidth/2*0.8f,0);
//移動path起點到(-mWidth/2*0.8f,0)
mPath.lineTo(mWidth/2*0.8f,0);
//直線終點為(mWidth/2*0.8f,0)
//y軸
mPath.moveTo(0,-mHeight/2*0.8f);
//移動path起點到(0,-mHeight/2*0.8f)
mPath.lineTo(0,mHeight/2*0.8f);
//直線終點為(0,mHeight/2*0.8f)
//x箭頭
mPath.moveTo(mWidth/2*0.8f*0.95f,-mWidth/2*0.8f*0.05f);
mPath.lineTo(mWidth/2*0.8f,0);
mPath.lineTo(mWidth/2*0.8f*0.95f,mWidth/2*0.8f*0.05f);
//y箭頭
mPath.moveTo(mWidth/2*0.8f*0.05f,mHeight/2*0.8f-mWidth/2*0.8f*0.05f);
mPath.lineTo(0,mHeight/2*0.8f);
mPath.lineTo(-mWidth/2*0.8f*0.05f,mHeight/2*0.8f-mWidth/2*0.8f*0.05f);
//繪制
Pathcanvas.drawPath(mPath,mPaint);
可以看出moveTo方法,可以移動下一次增加path的起點,而lineTo中的參數,即為直線的終點。
2、addArc與arcTo
方法 | 區別 |
---|---|
addArc | 畫一段圓弧 |
arcTo | 畫一段圓弧,當上一次的終點與圓弧起點未連接時,可以設置是否連接這兩點 |
addArc
r = Math.min(mWidth,mHeight)*0.6f/2;
mRectF.left = 0;
mRectF.top = -r;
mRectF.right = r;
mRectF.bottom = 0;
mPath.addArc(mRectF,-60,180);
//繪制
Pathcanvas.drawPath(mPath,mPaint);
再來看看arcTo
arcTomPath.moveTo(0,0);
mPath.arcTo(mRectF,-60,180);
//繪制
Pathcanvas.drawPath(mPath,mPaint);
可以看到arcTo多了一條從原點到圓弧起點的直線,而如果設置為
mPath.arcTo(mRectF,-60,180,false);
效果將和addArc相同。
三、圓角圖片以及更多形狀圖片
繼承ImageView,重寫父類的onSizeChanged方法,獲取View尺寸,之后根據View大小對圖片進行壓縮。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;
size();//切割尺寸計算
scaleBitmap();//壓縮圖片尺寸函數
}
在onDraw方法中進行樣式繪制,在其中使用clipPath的方法來實現圓角圖片。
@Override
protected void onDraw(Canvas canvas) {
canvas.translate(mViewWidth/2,mViewHeight/2);//將畫布坐標原點移動到中心位置
canvas.clipPath(pathFigure(), Region.Op.INTERSECT);//切割 mPath.reset();
canvas.drawBitmap(b,rect,rect,mPaint);
}
在scaleBitmap方法中對圖片的尺寸進行壓縮
private void scaleBitmap(){
Drawable drawable = getDrawable();//獲取圖片
if (drawable == null) {
return;
} if (getWidth() == 0 || getHeight() == 0) {
return;
} if (!(drawable instanceof BitmapDrawable)) {
return;
}
b = ((BitmapDrawable) drawable).getBitmap();//獲取bitmap
if (null == b) {
return;
}
float scaleWidth = (float) length/b.getWidth();
float scaleHeight = (float) length/b.getHeight();
matrix.postScale(scaleWidth,scaleHeight);//縮放矩陣
b=Bitmap.createBitmap(b,0,0,b.getWidth(),b.getHeight(),matrix,true);//壓縮圖片
}
在size方法中設置canvas的切割尺寸
protected void size(){
length = Math.min(mViewWidth,mViewHeight)/2;
rect = new Rect(-(int) length, -(int) length, (int) length, (int) length);//繪制圖片矩陣
}
現在就是發揮想象力的時候啦?(?),來編寫pathFigure()方法吧*
-
a、先編寫一個簡單的圓形圖片樣式
protected Path pathFigure(){ switch (modeFlag){ case CIRCLE: mPath.addCircle(0,0,length, Path.Direction.CW);//增加圓的path,順時針閉合圓 break; } return mPath; }
-
b、增加一個圓角圖片樣式
private RectF rectF = new RectF(); case ROUNDRECT: rectF.left = -length; rectF.top = -length; rectF.right = length; rectF.bottom = length; mPath.addRoundRect(rectF,radius,radius, Path.Direction.CW);//圓角矩形,radius為圓角的半徑,順時針閉合圓角矩形 break;
- c、再增加一個扇形樣式
(PS:為了可以獲得更多的圖片面積,需要把圓心下移一個length的距離,半徑擴大到之前的兩倍)
case SECTOR:
rectF.left = -length*2;
rectF.top = -length;
rectF.right = length*2;
rectF.bottom = length*3;
mPath.moveTo(0,length);
mPath.arcTo(rectF,angle,-angle*2-180);//繪制圓弧 break;
四、邏輯運算
兩條Path可通過多種邏輯運算進行結合,形成新的Path。
API如下:
op(Path path, Path.Op op)op(Path path1, Path path2, Path.Op op)
邏輯運算具有五種類型:
方法 | 描述 | 示意圖 |
---|---|---|
DIFFERENCE | B在A中的相對補集,即A減去A與B的交集 | 補集1
|
REVERSE_DIFFERENCE | A在B中的相對補集合,即B減去B與A的交集 | 補集2
|
INTERSECT | A與B的交集 | 交集
|
UNION | A與B的合集 | 合集
|
XOR | A與B的合集減去A與B的交集 | 異或
|
使用Path.op方法再給圓角圖片類,增加一種環形樣式:
case RING:
rectF.left = -length*2;
rectF.top = -length;
rectF.right = length*2;
rectF.bottom = length*3;
mPath1.moveTo(0,length);
mPath1.arcTo(rectF,angle,-angle*2-180);//較大的圓弧
rectF.left = -length/2;
rectF.top = length/2;
rectF.right = length/2;
rectF.bottom = length*3/2;
mPath2.moveTo(0,length);
mPath2.arcTo(rectF,angle,-angle*2-180);//較小的圓弧
mPath.op(mPath1,mPath2, Path.Op.XOR);//異或獲取環形
五、小結
本文介紹了Path的基本使用方法與邏輯運算,同時通過圓角圖片的例子,進行了實戰。使用Path方法,還可以增加更多有趣的圖形,比如star,多邊形,格子圖等等。如果在閱讀過程中,有任何疑問與問題,歡迎與我聯系。
原作者博客地址
GitHub:https://github.com/Idtk
博客:http://www.idtkm.com
郵箱:Idtkma@gmail.com
PS: 示例中使用的方法,相對消耗內存,更合適的是設置反向填充來完成圓角圖片的生成,FigureImageView為反向填充的方法,OldFigureImageView為示例中的方法 源碼涉及知識