今天通過自定義View來實作一個帶進度的圓形進度條,實作的最終效果如下圖所示:
現在來講一下設計的思路:首先這個進度條可以自定義小圓角矩形的數量、小圓角矩形大小、小圓角矩形的圓角角度、未完成進度時的顔色,完成進度時的顔色、文字大小、文字顔色、圓形半徑,是以需要自定義這些參數;那如何畫這個圓形進度呢?我們需要先畫一個小圓角矩形,再旋轉畫布再畫矩形,如圖這裡有12個小圓角矩形,每次旋轉360/12=30度,畫一個小圓角矩形,共畫12個。然後畫中間的進度值,畫中間的進度值,主要需要計算文本的位置,這個稍後再介紹。下面通過代碼進行詳細的講解下實作過程。代碼如下:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.support.annotation.FloatRange;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* Created by 劉信 on 2018/4/27.
*/
public class LoadView extends View {
//已經load的顔色值
private int mHasLoadColor;
//沒有load的顔色值
private int mNormalLoadColor;
//小長方形的寬度
private int mRectangleWidth;
//小長方形的高度
private int mRectangleHeight;
//小長方形的個數
private int mRectangleNum;
//小長方形圓角度
private int mRectangleRadius;
//内半徑大小
//内半徑指不包括方塊的大小
private int mInnerRadius;
//外半徑
private int mOuterRadius;
//文字大小
private int mTextSize;
//畫筆
private Paint mPaint;
//文字的畫筆
private TextPaint mTextPaint;
//小長方形
private RectF mRectF;
private Context mContext;
//百分比字元串
private String mPercentStr;
//百分比
private float mPercent;
public LoadView(Context context) {
this(context, null);
}
public LoadView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, );
}
public LoadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int deStyleAttr) {
mContext = context;
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.LoadView, deStyleAttr, );
//下面是一些自定義參數的預設值
mHasLoadColor = mContext.getResources().getColor(R.color.color_3398ab);
mNormalLoadColor = mContext.getResources().getColor(R.color.color_e0e0e0);
mRectangleWidth = dipToPixels(mContext, );
mRectangleHeight = dipToPixels(mContext, );
mRectangleNum = ;
mInnerRadius = dipToPixels(mContext, );
mTextSize = dipToPixels(mContext, );
mRectangleRadius = dipToPixels(mContext, );
mPercentStr = "0%";
mPercent = f;
if (typedArray != null) {
//從xml中讀取這些參數設定
mHasLoadColor = typedArray.getColor(R.styleable.LoadView_HasLoadColor, mHasLoadColor);
mNormalLoadColor = typedArray.getColor(R.styleable.LoadView_NormalLoadColor, mNormalLoadColor);
mRectangleWidth = typedArray.getDimensionPixelSize(R.styleable.LoadView_RectangleWidth, mRectangleWidth);
mRectangleHeight = typedArray.getDimensionPixelSize(R.styleable.LoadView_RectangleHeight, mRectangleHeight);
mRectangleNum = typedArray.getInteger(R.styleable.LoadView_RectangleNum, mRectangleNum);
mInnerRadius = typedArray.getDimensionPixelSize(R.styleable.LoadView_InnerRadius, mInnerRadius);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.LoadView_TextSize, mTextSize);
mRectangleRadius = typedArray.getDimensionPixelSize(R.styleable.LoadView_RectangleRadius, mRectangleRadius);
typedArray.recycle();
}
//外半徑等于使用者設定的内半徑加上小矩形的高度
mOuterRadius = mInnerRadius + mRectangleHeight;
//這裡是畫第一個圓形矩形需要的RetcF。通過左上角和右下角坐标确定,
// 這裡左上角坐标(mOuterRadius - mRectangleWidth / 2f,0),
//右下角坐标(mOuterRadius + mRectangleWidth / 2f, mRectangleHeight);
mRectF = new RectF(mOuterRadius - mRectangleWidth / f, , mOuterRadius + mRectangleWidth / f, mRectangleHeight);
mPaint = new Paint();
mPaint.setColor(mNormalLoadColor);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mTextPaint = new TextPaint();
//這裡還可以設定文本的字型,我去掉了
/*try {
Typeface typeface = Typeface.createFromFile("fonts/MyriadPro-Bold.otf");
mTextPaint.setTypeface(typeface);
} catch (Exception e) {
e.printStackTrace();
}*/
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mHasLoadColor);
}
private int dipToPixels(Context context, float dip) {
final float SCALE = context.getResources().getDisplayMetrics().density;
float valueDips = dip;
int valuePixels = (int)(valueDips * SCALE + f);
return valuePixels;
}
/**
*供外界調用
* 請在主線程調用,設定進度 0到1
* @param percent
*/
public void setPercent(@FloatRange(from=, to=) float percent){
this.mPercent=percent;
this.mPercentStr=Math.round(percent*)+"%";
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//強制寬高度,在xml檔案中設定寬高無效
//設定大小請根據半徑、小長方形大小來設定
int defaultSize = * (mInnerRadius + mRectangleHeight);
setMeasuredDimension(defaultSize, defaultSize);
}
@Override
protected void onDraw(Canvas canvas) {
int hasDownCount = ;//已經下載下傳百分比換算成需要畫多少個已經load的長方形
hasDownCount = Math.round(mPercent * mRectangleNum);//才用四舍五入
//旋轉之前先儲存下畫布的狀态
canvas.save();
//每次旋轉需要的角度
float degress = f / mRectangleNum;
for (int i = ; i < mRectangleNum; i++) {
if (hasDownCount > i) {
//已經load的顔色
mPaint.setColor(mHasLoadColor);
} else {
//沒有load的顔色
mPaint.setColor(mNormalLoadColor);
}
//畫圓角矩形
canvas.drawRoundRect(mRectF, mRectangleRadius, mRectangleRadius, mPaint);
//以中心點旋轉畫布,這裡想象下你現實中拿隻筆在紙上畫,每畫一個你就旋轉紙,你就知道了
canvas.rotate(degress, mOuterRadius, mOuterRadius);
}
//回到之前的畫布狀态
canvas.restore();
//文本寬度
float textWidth = mTextPaint.measureText(mPercentStr);
//計算高度要注意了
//drawText是以文本的BaseLine為準的
//是以計算高度步驟:文本高度(descent-ascent)的一半減去descent;得到的結果
// 是Baseline離中心圓點的偏移量,再加上半徑就是高度了
//還不懂?看下面的圖
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float diffBaseLine = (-(fontMetrics.ascent + fontMetrics.descent)) / ;
//x預設是這個字元串的左邊在螢幕的位置,如果設定了paint.setTextAlign(Paint.Align.CENTER);那就是字元的中心,y是指定這個字元baseline在螢幕上的位置
canvas.drawText(mPercentStr, mOuterRadius - textWidth / , mOuterRadius + diffBaseLine, mTextPaint);
}
}
代碼已上傳,連結位址LoadView