此次自定義不同的進度條

此次新項目來了一個不一樣尋常進度條,效果圖以下,廢話很少說,直接從進度條背景、虛線圓弧、漸變背景圓弧、進度數值顯示、文字介紹、進度條當前點android

效果圖

本篇主要涉及到的方法以下:drawArc、drawCircle、drawText,涉及的類Rect、RectFcanvas

進度條背景

相關變量的設置,能夠根據本身的需求給這些變量加set\get方法以及style到xml對應的屬性設置,這裏不實現數組

//圓弧開始的角度
    private float startAngle = 170;
    //圓弧結束的角度
    private float endAngle = 45;
    //圓弧背景的開始和結束間的夾角大小
    private float mAngle = 200;
    //當前進度夾角大小
    private float mIncludedAngle = 0;
複製代碼

建立Paint設置對應的屬性bash

mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //抗鋸齒
        mArcPaint.setAntiAlias(true);
        //圓弧的背景色
        mArcPaint.setColor(Color.parseColor("#14FFFFFF"));
        //設置透明度(數值爲0-255)
//        mArcPaint.setAlpha(100);
        //設置畫筆的畫出的形狀
        mArcPaint.setStrokeJoin(Paint.Join.ROUND);
        mArcPaint.setStrokeCap(Paint.Cap.ROUND);
        //設置畫筆類型
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setStrokeWidth(dp2px(mStrokeWith));
複製代碼

繪製默認的圓弧的背景ide

//繪製圓弧背景
        RectF mRectF = new RectF(mStrokeWith + dp2px(5), mStrokeWith + dp2px(5), getWidth() - mStrokeWith - dp2px(5), getHeight() - mStrokeWith);
        canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
        
複製代碼

繪製進度圓弧函數

//根據當前數據繪製對應的圓弧
        canvas.drawArc(mRectF, startAngle, mIncludedAngle, false, mArcPaint);
        
複製代碼

虛線圓弧

虛線圓弧與實現的圓弧的方法是同樣的,主要是設置虛線方法setPathEffect,咱們這裏用DashPathEffect,構造函數DashPathEffect(float intervals[], float phase),intervals:控制實線和實線以後空白線的寬度(數組長度必須爲偶數)phase: 將View向」左「偏移phase佈局

代碼實現post

//繪製圓弧背景
        mArcPaint.setStrokeWidth(dp2px(mPointStrokeWith));
        //設置虛線
        mArcPaint.setPathEffect(new DashPathEffect(new float[]{dp2px(1), dp2px(6)}, 0));
        RectF mRectF = new RectF(mPointStrokeWith + dp2px(20), mPointStrokeWith + dp2px(20), getWidth() - mPointStrokeWith - dp2px(20), getHeight() - mPointStrokeWith - dp2px(20));
        canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
複製代碼

漸變背景圓弧

這裏的漸變咱們用的是LinearGradient線性漸變,Paint的初始化見最後的所有代碼優化

而後給Paint設置漸變背景動畫

linearGradient = new LinearGradient(0, dp2px(40), 0, height / 2, changeColors, null, Shader.TileMode.CLAMP);
        mBkgArcPaint.setShader(linearGradient);

複製代碼

最終用設置好的畫筆去繪製漸變背景

//繪製圓弧背景
        RectF mRectF = new RectF(mPointStrokeWith + dp2px(33), mPointStrokeWith + dp2px(33), getWidth() - mPointStrokeWith - dp2px(33), getHeight() - mPointStrokeWith - dp2px(33));
        canvas.drawArc(mRectF, startAngle, mAngle, false, mBkgArcPaint);
複製代碼

進度數值顯示、文字介紹

進度數值、文字介紹就要用到drawText去繪製文本了,mTextPaint也是設置畫筆對應的屬性值,而後開始畫

Rect mRect = new Rect();
        String mValue = String.valueOf(mAnimatorValue);
        //繪製中心的數值
        mTextPaint.setTextSize(dp2px(40));
        mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
        canvas.drawText(String.valueOf(mAnimatorValue), centerX, centerY - 0.2f * mRect.height(), mTextPaint);

        //繪製中心數值的百分比
        mTextPaint.setTextSize(dp2px(20));
        mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
        //x軸:centerX+繪製中心的數值的寬度/2
        canvas.drawText(mChar, centerX + width / 5, centerY - height / 30, mTextPaint);
//        canvas.drawText(mChar, centerX + mRect.width() / 2.0f + 1.6f * mRect.height(), centerY - 0.8f * mRect.height(), mTextPaint);

        //繪製中心文字描述
        mTextPaint.setTextSize(dp2px(14));
        mTextPaint.getTextBounds(mDes, 0, mDes.length(), mRect);
        canvas.drawText(mDes, centerX, centerY + 1.6f * mRect.height(), mTextPaint);


複製代碼

進度條當前點

繪製進度條當前點,其實就是大圓弧所在點的座標,以正東面爲0度起點計算指定角度所對應的圓周上的點的座標:要用到的方法以下:

float radian = Math.toRadians(angle);
float x = center.x + Math.cos(radian)*currentAdius;
float y = center.y + Math.sin(radian)*currentAdius;

複製代碼

說明angle爲弧度,center.x爲大圓弧圓心所在的X座標,center.y爲大圓弧圓心所在Y座標,currentAdius爲圓的半徑

獲得x,y座標繪製圓就畫出了進度條當前點,因爲效果圖是外層白色裏面一個紅色,則須要畫兩個圓

//根據當前數據繪製對應的夫圓
        float radian = (float) Math.toRadians(startAngle + mIncludedAngle);
        float x = (float) (centerX + (pRadius - radius) * Math.cos(radian));
        float y = (float) (centerY + (pRadius - radius) * Math.sin(radian));
        canvas.drawCircle(x, y, radius, mPCirclePaint);
        //根據當前數據繪製對應的子圓
        canvas.drawCircle(x, y, radius / 2, mCCirclePaint);
        
複製代碼

至此效果圖的效果就作出來了,再給效果圖的進度變化及其數值變化加個動畫

動態設置的方法

/**
     * 設置數據
     *
     * @param minValue     最小值
     * @param maxValue     最大值
     * @param currentValue 當前繪製的值
     * @param des          描述信息
     */
    public void setValues(int minValue, int maxValue, int currentValue, String des) {
        mDes = des;
        mMaxValue = maxValue;
        mMinValue = minValue;
        //徹底覆蓋
        if (currentValue > maxValue) {
            currentValue = maxValue;
        }
        //計算弧度比重
        float scale = (float) currentValue / maxValue;
        //計算弧度
        float currentAngle = scale * mAngle;
        //開始執行動畫
        setAnimation(0, currentAngle, currentValue, time);
    }
    
複製代碼

動畫的方法

/**
     * 爲繪製弧度及數據設置動畫
     *
     * @param startAngle   開始的弧度
     * @param currentAngle 須要繪製的弧度
     * @param currentValue 須要繪製的數據
     * @param time         動畫執行的時長
     */
    private void setAnimation(float startAngle, float currentAngle, int currentValue, int time) {
        //繪製當前數據對應的圓弧的動畫效果
        ValueAnimator progressAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
        progressAnimator.setDuration(time);
        progressAnimator.setTarget(mIncludedAngle);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mIncludedAngle = (float) animation.getAnimatedValue();
                //從新繪製,否則不會出現效果
                postInvalidate();
            }
        });
        //開始執行動畫
        progressAnimator.start();
        //中心數據的動畫效果
        ValueAnimator valueAnimator = ValueAnimator.ofInt(mAnimatorValue, currentValue);
        valueAnimator.setDuration(time);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mAnimatorValue = (int) valueAnimator.getAnimatedValue();
                postInvalidate();
            }
        });
        valueAnimator.start();
    }
    
複製代碼

至此效果圖就動起來了,我複用了mArcPaint屢次,能夠根據本身的習慣多建立幾個Paint,總體寫完了代碼奉上,你們可本身再優化一下

自定義進度條完整代碼

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.LinearInterpolator;

/**
 * @author madreain
 * @date 2019-07-12.
 * module:
 * description:
 */
public class ArcView extends View {

    //根據數據顯示的圓弧Paint
    private Paint mArcPaint;
    //背景圓
    private Paint mBkgArcPaint;
    //圓點
    private Paint mPCirclePaint;
    private Paint mCCirclePaint;
    private float radius = dp2px(6);
    //漸變處理
    private LinearGradient linearGradient;
    //漸變的顏色
    private int[] changeColors = new int[]{Color.parseColor("#FFFFFF"), Color.parseColor("#00FFFFFF")};
    //文字描述的paint
    private Paint mTextPaint;
    //圓弧開始的角度
    private float startAngle = 170;
    //圓弧結束的角度
    private float endAngle = 45;
    //圓弧背景的開始和結束間的夾角大小
    private float mAngle = 200;
    //當前進度夾角大小
    private float mIncludedAngle = 0;
    //圓弧的畫筆的寬度
    private float mStrokeWith = 4;
    //斷點圓弧的畫筆的寬度
    private float mPointStrokeWith = 1;
    //中心的文字描述
    private String mDes = "";
    //中心的文字描述邊上的百分號
    private String mChar = "%";
    //動畫效果的數據及最大/小值
    private int mAnimatorValue, mMinValue, mMaxValue;
    //中心點的XY座標
    private float centerX, centerY;
    //最外層大圓的半徑
    private float pRadius;
    private float height, width;
    //動畫時間
    private int time = 10000;

    public ArcView(Context context) {
        this(context, null);
        //初始化paint
        initPaint();
    }

    public ArcView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
        //初始化paint
        initPaint();
    }

    public ArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //初始化paint
        initPaint();
    }

    private void initPaint() {
        //圓弧的paint
        mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //抗鋸齒
        mArcPaint.setAntiAlias(true);
        //圓弧的背景色
        mArcPaint.setColor(Color.parseColor("#14FFFFFF"));
        //設置透明度(數值爲0-255)
//        mArcPaint.setAlpha(100);
        //設置畫筆的畫出的形狀
        mArcPaint.setStrokeJoin(Paint.Join.ROUND);
        mArcPaint.setStrokeCap(Paint.Cap.ROUND);
        //設置畫筆類型
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setStrokeWidth(dp2px(mStrokeWith));

        //夫圓點
        mPCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //抗鋸齒
        mPCirclePaint.setAntiAlias(true);
        //圓弧的背景色
        mPCirclePaint.setColor(Color.parseColor("#FFFFFF"));
        //設置畫筆的畫出的形狀
        mPCirclePaint.setStrokeJoin(Paint.Join.ROUND);
        mPCirclePaint.setStrokeCap(Paint.Cap.ROUND);
        //設置畫筆類型
        mPCirclePaint.setStyle(Paint.Style.FILL);

        //子圓點
        mCCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //抗鋸齒
        mCCirclePaint.setAntiAlias(true);
        //圓弧的背景色
        mCCirclePaint.setColor(Color.parseColor("#FF6672"));
        //設置畫筆的畫出的形狀
        mCCirclePaint.setStrokeJoin(Paint.Join.ROUND);
        mCCirclePaint.setStrokeCap(Paint.Cap.ROUND);
        //設置畫筆類型
        mCCirclePaint.setStyle(Paint.Style.FILL);


        //背景填充圓弧的paint
        mBkgArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //抗鋸齒
        mBkgArcPaint.setAntiAlias(true);
        //圓弧的背景色
        mBkgArcPaint.setColor(Color.parseColor("#4DFFFFFF"));
        //設置畫筆類型
        mBkgArcPaint.setStyle(Paint.Style.FILL);

        //中心文字的paint
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.parseColor("#FFFFFF"));
        //設置文本的對齊方式
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        //mTextPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.dp_12));
        mTextPaint.setTextSize(dp2px(40));

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        height = h;
        width = w;
        pRadius = w / 2;
        centerX = w / 2;
        centerY = h / 2;
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //繪製弧度
        drawArc(canvas);
        //繪製斷點弧度
        drawPointArc(canvas);
        //繪製背景圓
        drawBkgArc(canvas);
        //繪製文本
        drawText(canvas);
    }

    /**
     * 繪製背景圓
     *
     * @param canvas
     */
    private void drawBkgArc(Canvas canvas) {
        //繪製圓弧背景
        //漸變背景
        linearGradient = new LinearGradient(0, dp2px(40), 0, height / 2, changeColors, null, Shader.TileMode.CLAMP);
        mBkgArcPaint.setShader(linearGradient);
        RectF mRectF = new RectF(mPointStrokeWith + dp2px(33), mPointStrokeWith + dp2px(33), getWidth() - mPointStrokeWith - dp2px(33), getHeight() - mPointStrokeWith - dp2px(33));
        canvas.drawArc(mRectF, startAngle, mAngle, false, mBkgArcPaint);
    }


    /**
     * 繪製文本
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        Rect mRect = new Rect();
        String mValue = String.valueOf(mAnimatorValue);
        //繪製中心的數值
        mTextPaint.setTextSize(dp2px(40));
        mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
        canvas.drawText(String.valueOf(mAnimatorValue), centerX, centerY - 0.2f * mRect.height(), mTextPaint);

        //繪製中心數值的百分比
        mTextPaint.setTextSize(dp2px(20));
        mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
        //x軸:centerX+繪製中心的數值的寬度/2
        canvas.drawText(mChar, centerX + width / 5, centerY - height / 30, mTextPaint);
//        canvas.drawText(mChar, centerX + mRect.width() / 2.0f + 1.6f * mRect.height(), centerY - 0.8f * mRect.height(), mTextPaint);

        //繪製中心文字描述
        mTextPaint.setTextSize(dp2px(14));
        mTextPaint.getTextBounds(mDes, 0, mDes.length(), mRect);
        canvas.drawText(mDes, centerX, centerY + 1.6f * mRect.height(), mTextPaint);

        //繪製最小值
//        String minValue=String.valueOf(mMinValue);
//        String maxValue=String.valueOf(mMaxValue);
//        mTextPaint.setTextSize(dp2px(18));
//        mTextPaint.getTextBounds(minValue,0,minValue.length(),mRect);
//        canvas.drawText(minValue, (float) (centerX-0.6*centerX-dp2px(5)), (float) (centerY+0.75*centerY+mRect.height()+dp2px(5)),mTextPaint);
//        //繪製最大指
//        mTextPaint.getTextBounds(maxValue,0,maxValue.length(),mRect);
//        canvas.drawText(maxValue, (float) (centerX+0.6*centerX+dp2px(5)), (float) (centerY+0.75*centerY+mRect.height()+dp2px(5)),mTextPaint);
    }

    /**
     * 繪製當前的斷點圓弧
     *
     * @param canvas
     */
    private void drawPointArc(Canvas canvas) {
        //繪製圓弧背景
        mArcPaint.setStrokeWidth(dp2px(mPointStrokeWith));
        //設置虛線
        mArcPaint.setPathEffect(new DashPathEffect(new float[]{dp2px(1), dp2px(6)}, 0));
        RectF mRectF = new RectF(mPointStrokeWith + dp2px(20), mPointStrokeWith + dp2px(20), getWidth() - mPointStrokeWith - dp2px(20), getHeight() - mPointStrokeWith - dp2px(20));
        canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
    }

    /**
     * 繪製當前的圓弧
     *
     * @param canvas
     */
    private void drawArc(Canvas canvas) {
        mArcPaint.setStrokeWidth(dp2px(mStrokeWith));
        mArcPaint.setPathEffect(null);
        //圓弧的背景色
        mArcPaint.setColor(Color.parseColor("#14FFFFFF"));
        //繪製圓弧背景
        RectF mRectF = new RectF(mStrokeWith + dp2px(5), mStrokeWith + dp2px(5), getWidth() - mStrokeWith - dp2px(5), getHeight() - mStrokeWith);
        canvas.drawArc(mRectF, startAngle, mAngle, false, mArcPaint);
        //繪製當前數值對應的圓弧
        mArcPaint.setColor(Color.parseColor("#FFFFFF"));
        //根據當前數據繪製對應的圓弧
        canvas.drawArc(mRectF, startAngle, mIncludedAngle, false, mArcPaint);
        //根據當前數據繪製對應的夫圓
        float radian = (float) Math.toRadians(startAngle + mIncludedAngle);
        float x = (float) (centerX + (pRadius - radius) * Math.cos(radian));
        float y = (float) (centerY + (pRadius - radius) * Math.sin(radian));
        canvas.drawCircle(x, y, radius, mPCirclePaint);
        //根據當前數據繪製對應的子圓
        canvas.drawCircle(x, y, radius / 2, mCCirclePaint);
    }

    /**
     * 爲繪製弧度及數據設置動畫
     *
     * @param startAngle   開始的弧度
     * @param currentAngle 須要繪製的弧度
     * @param currentValue 須要繪製的數據
     * @param time         動畫執行的時長
     */
    private void setAnimation(float startAngle, float currentAngle, int currentValue, int time) {
        //繪製當前數據對應的圓弧的動畫效果
        ValueAnimator progressAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
        progressAnimator.setDuration(time);
        progressAnimator.setTarget(mIncludedAngle);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mIncludedAngle = (float) animation.getAnimatedValue();
                //從新繪製,否則不會出現效果
                postInvalidate();
            }
        });
        //開始執行動畫
        progressAnimator.start();
        //中心數據的動畫效果
        ValueAnimator valueAnimator = ValueAnimator.ofInt(mAnimatorValue, currentValue);
        valueAnimator.setDuration(time);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mAnimatorValue = (int) valueAnimator.getAnimatedValue();
                postInvalidate();
            }
        });
        valueAnimator.start();
    }

    /**
     * 設置數據
     *
     * @param minValue     最小值
     * @param maxValue     最大值
     * @param currentValue 當前繪製的值
     * @param des          描述信息
     */
    public void setValues(int minValue, int maxValue, int currentValue, String des) {
        mDes = des;
        mMaxValue = maxValue;
        mMinValue = minValue;
        //徹底覆蓋
        if (currentValue > maxValue) {
            currentValue = maxValue;
        }
        //計算弧度比重
        float scale = (float) currentValue / maxValue;
        //計算弧度
        float currentAngle = scale * mAngle;
        //開始執行動畫
        setAnimation(0, currentAngle, currentValue, time);
    }

    public float dp2px(float dp) {
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        return dp * metrics.density;
    }
}


複製代碼

xml佈局好,在Activity中使用調用方法,就能看見動畫了

arcview.setValues(0,100,100,"預埋與隱蔽");
複製代碼
相關文章
相關標籤/搜索