android:自定義圓環形百分比控件,代碼簡單,加載動畫,代碼詳細說明

需求要寫一個環形百分比,是這樣的:

   

第一張是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,這個是要自己計算的,同理能得出畫扇形所需要的矩形,注意讓矩形位置居中。自己畫了張圖,額,字比較醜,將就一下

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章