需求要寫一個環形百分比,是這樣的:
第一張是UI圖,第二張做出來後,在layout上渲染出來的圖。
首先,需要知道android的座標系是怎麼樣的,左上爲(0,0)往右x軸增大,往下y軸增大
然後就直接上代碼啦,變量不多,都有註釋,也沒有弄AttributeSet,想弄layout設置參數的,可以自己加一下,style文件寫好配置,layout文件裏設置,然後自定義控件裏獲取。
public class CirclePercentView extends View {
// 圓畫筆
private Paint circlePaint;
// 圓環畫筆
private Paint ringPaint;
// 百分數畫筆
private Paint textPaint;
//動態設置的底環顏色
private String circleColor;
//動態設置圓環顏色
private String ringColor;
private float percent = 35.2f;//設一個初始的百分比,超過0的話,能在Android studio layout文件下直接看到效果
//控件寬度
private int mWidth;
//畫圓環需要的RectF,因爲只需要生成一次,假如刷新界面的話,他就會一直生成,雖然不影響,但是把它拿出來全局
private RectF rectF;
//文字大小
private float textSize;
//圓環寬度
private int stroke;
public CirclePercentView(Context context) {
super(context);
init();
}
public CirclePercentView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CirclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public CirclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
circlePaint = new Paint();
circlePaint.setAntiAlias(true);//設置抗鋸齒
circlePaint.setStyle(Paint.Style.STROKE);//設置繪畫風格爲邊框
//這個是個dp轉px的方法,不嚴格的話可以隨便寫個10,12,15隨意
stroke = AppUtils.dip2px(getContext(), 10);
circlePaint.setStrokeWidth(stroke);//設置邊框寬度
circlePaint.setColor(getResources().getColor(R.color.blue_ff));//設置顏色,可以替換成Color.parseColor("#xxxxxx")
ringPaint = new Paint();
ringPaint.setAntiAlias(true);
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setStrokeWidth(stroke);
ringPaint.setColor(getResources().getColor(R.color.blue_24));//可以替換成Color.parseColor("#xxxxxx")
textPaint = new Paint();
textPaint.setStyle(Paint.Style.FILL);//設置風格爲充滿
textPaint.setAntiAlias(true);
textPaint.setColor(getResources().getColor(R.color.black));
textSize = AppUtils.dip2px(getContext(), 14);
textPaint.setTextSize(textSize);
}
/**
* 設置顏色和中間的文字
* @param color 這個color是字符串,不帶#號6位
* @param percent 傳0-100的
*/
public void setTextColor(String color, double percent) {
circleColor = "#80" + color;//讓背景環顏色爲環顏色透明一些,
ringColor = "#" +color;
startAnimator((float) percent);//開啓動畫
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
int mHeight = getHeight();
if(mWidth > mHeight){
mWidth = mHeight;//獲取最短的一邊
}
rectF = new RectF();//rectF爲畫扇形所需要的矩形,扇形會在矩形內
//rectF長寬爲right-left或bottom-top,因爲是正方形,都一樣
//因爲畫筆使用了stroke,他半徑不爲控件的一半了,不設置stroke的話長寬都是是圓半徑*2,
// 長寬 = mWidth - stroke,
//因爲rectF範圍不是滿控件了,所以位置不能是0 0不然畫出來,會偏左上角,
// 爲了使rectF居中,都往右下角偏移了stroke/2,所以也要少減stroke/2,
rectF.left = stroke/2;
rectF.top = stroke/2;
//知道了長寬,通過左上可以計算右下
rectF.right = mWidth - stroke/2;
rectF.bottom = mWidth - stroke/2;
//可以自己設置成rectF.left = 0; rectF.top = 0; rectF.right = mWidth - stroke; rectF.bottom = mWidth - stroke
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (circleColor != null) {
circlePaint.setColor(Color.parseColor(circleColor));//如果有顏色的話,設置顏色
}
if (ringColor != null) {
ringPaint.setColor(Color.parseColor(ringColor));//如果有顏色的話,設置顏色顏色
}
//畫外層圓環 當畫筆設置了 StrokeWidth 時,圓的半徑=內圓的半徑+StrokeWidth/2
canvas.drawCircle(mWidth/2, mWidth/2, (mWidth/2 - stroke) + stroke/2, circlePaint);
float point = percent*360/100;
if (point > 360) {
point = 360;
}
//rectF扇形範圍,-90,從上開始繪畫,比如改成0,就是從左開始繪畫,順時針,point繪畫多少角度
// false是去掉扇形的半徑繪畫,ringPaint畫筆
canvas.drawArc(rectF, -90, point, false, ringPaint);
String str = percent + "%";//需要自定義文字的話,就定義一個全局str,從外面傳進來
float textWid = textPaint.measureText(str);
//str文字,第二個和第三個參數是爲了使他居中mWidth/2 - textWid/2:從x軸的哪裏開始繪畫,應該很好計算
//mWidth/2 + textSize/2 之所以是+是因爲文字是從左下角開始繪製的,開始以爲是從上到下,寫成了-,就偏上了
canvas.drawText(str, mWidth/2 - textWid/2, mWidth/2 + textSize/2, textPaint);
}
private void startAnimator(float process) {
ValueAnimator animator = ValueAnimator.ofFloat(0, process);//從百分0到process的過程
animator.setDuration(800);//持續時間
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
percent = (Float) animation.getAnimatedValue();//當這個動畫過程進行過程中,獲取過程中的數值
BigDecimal b = new BigDecimal((Float) animation.getAnimatedValue());
percent = b.setScale(2, ROUND_DOWN).floatValue();//保留兩位小數
invalidate();//重繪,會重新走onDraw,onDraw裏是根據percent繪畫的,所以界面會改變
}
});
animator.start();
}
}
View是先走onMeasure ->onSizeChanged->onDraw 因此,在onSizeChange獲取控件寬度。
動畫的原理是,讓角度慢慢增長,通過使百分比從0開始,一段時間增長至所設置的百分比,在百分比改變的時候,將界面進行重繪
當畫圓和扇形,畫筆設置成了stroke,它的半徑就不是想當然的控件寬度除以2了,假如是像上面代碼圓環充滿控件,它的寬度就是控件寬度-環的寬度除以2,這個是要自己計算的,同理能得出畫扇形所需要的矩形,注意讓矩形位置居中。自己畫了張圖,額,字比較醜,將就一下