- 一:概述:
android的自定義view提供了很多很豐富的畫布操作,這幾天公司的項目需要寫一個圓形進度條,藉着這個機會,說一下畫布的基本操作- 二:預期效果
我要實現的效果如圖:
![]()
這是一個等級效果圖,要求可以根據用戶的等級,動態的改變藍色進度條的停留位置。
三:實現思路:
canvas可以畫很多基本的圖形:長方形,圓形,圓角長方形,扇形…
根據圖片我第一時間想到的就是用畫弧度實現,也就是扇形。
由於需要有一個劃過的動畫,所以不能一次就畫完,一次畫一點,分多次畫完。
這裏有兩種思路:
1.起始位置一樣,每次畫的弧度不同。
2.每次畫的弧度一樣,起始位置動態計算。
第一種思路實現起來簡單粗暴,在這裏直接選擇第一種。
這個自定義view的難點就是在動畫完成的時候計算結束位置有些許的麻煩,用到了一些幾何知識,如果忘了就去翻翻初中的數學課本吧。
四:代碼實現:
我們定義的一些變量,我都寫了註釋:
private RectF rectF;
private double sweepRadius = 0;//掃過的角度(默認是0)
boolean isRunning;//運行的控制器
float radius = 1f;//每一幀畫完之後的角度
float speedRadius = 10f;//每次畫的角度(可以理解爲速度)
float starttPoint = 270f;//初始化的角度(默認在中間頂部
private Paint mPaint;
private int paintWidth = 2;//畫筆的寬度
float finalRadius=0;
double pi =3.14159265358979323846264338327950288419716939937510582097494459230781640628620899;//定義圓周率
首先在初始化的時候,我們做了一些操作
mPaint = new Paint();
mPaint.setColor(0xff1ecfff);//畫筆的顏色
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(paintWidth);//畫筆的寬度
mPaint.setAntiAlias(true);//設置抗鋸齒
在onSizeChanged函數中初始化畫筆的繪畫區域矩形rectF
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
rectF = new RectF(paintWidth,paintWidth,getWidth()-paintWidth,getHeight()-paintWidth);
}
下邊就是我們的核心ondraw方法了,裏邊有一些邏輯:我都做了詳細的註釋
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);//畫布背景顏色
if (sweepRadius==0) return;//如果掃過的角度爲0,則不用畫
if (finalRadius!=0){
canvas.drawArc(rectF, starttPoint, finalRadius, false, mPaint);
return;
}
canvas.drawArc(rectF, starttPoint, radius, false, mPaint);
radius=radius+speedRadius ;
if (radius >= sweepRadius+1f){
//畫圓結束
finalRadius = radius-speedRadius;
isRunning = false;
sweepRadius = finalRadius;//重新確切的告訴view掃過的角度
getStarPos();//計算畫完後的座標的方法
if (listener!=null){
listener.onDrawFinish(starX , starY);
}
}
if (!isRunning) return;
//刷新view,每50毫秒執行一次ondraw()
postInvalidateDelayed(50);
}
在畫圈完成後我們有一個計算停留位置的方法getStarPos(),在計算的過程中,我們用到了圓周率,都是一些死方法,代碼如下:
private double starX;// x座標
private double starY;// y座標
/**
* 得到畫圈後的座標點
*/
public void getStarPos(){
double r = (getWidth()-paintWidth*2)/2;//半徑
if (sweepRadius==0){
starX = r;
starY = 0;
}else if (sweepRadius < 90d){
starX = r + Math.sin(sweepRadius*pi/180)*r;
starY = r - Math.cos(sweepRadius*pi/180)*r;
}else if (sweepRadius == 90d){
starX = r*2;
starY = r;
}else if (sweepRadius < 180d){
starX = r+Math.cos((sweepRadius-90d)*pi/180)*r;
starY = r+Math.sin((sweepRadius-90d)*pi/180)*r;
}else if (sweepRadius == 180d){
starX = r;
starY = r*2;
}else if (sweepRadius < 270d){
starX = r - Math.sin((sweepRadius-180d)*pi/180)*r;
starY = r + Math.cos((sweepRadius-180d)*pi/180)*r;
}else if (sweepRadius == 270d){
starX = 0;
starY = r;
}else if (sweepRadius < 360d){
starX = r - Math.cos((sweepRadius-270d)*pi/180)*r;
starY = r - Math.sin((sweepRadius-270d)*pi/180)*r;
}
}
爲了把畫完後的座標傳給外界,定義了一個藉口監聽
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
private OnDrawingFinishListener listener;
public void setOnDrawingFinishListener(OnDrawingFinishListener onDrawingFinishListener){
this.listener = onDrawingFinishListener;
}
public interface OnDrawingFinishListener{
void onDrawFinish(double xPos, double yPos);
}
以上就是我們的全部實現過程了。如有那些不理解或認爲不妥的地方,請在下方給我留言。
下邊是源碼地址,裏邊有詳細的用法。
github下載源碼