1. 程式人生 > >Android自定義帶動畫圓環進度條

Android自定義帶動畫圓環進度條

1.首先是自定義類

package com.yx.yxcustomprogress;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.util.ArrayList;

public class ColorfulRingProgressView extends View {
    private float           mPercent        = 0f;//進度值預設為0 最大值為100
    private float           mStrokeWidth    = 0f;//圓環寬度
    private int             mBgColor        = 0xffe1e1e1;//預設背景顏色
    private float           mStartAngle     = 0f;//起點位置
    private int             mFgColorStart   = 0xffffe400;//漸變開始顏色
    private int             mFgColorEnd     = 0xffff4800;//漸變結束顏色
    private LinearGradient  mShader         = null;//線性漸變
    private Context         mContext        = null;
    private RectF           mOval           = null;
    private Paint           mPaint          = null;
    public ArrayList<XYZ>   mList           = null;//施工節點座標集合
    private Float           r               = null;//半徑
    private int             distance        = 100;//進度圈離邊界的距離
    private int             stage           = 50;//施工階段離進度圈的距離
    private int             divide          = 6;//將圓等分成幾份
    private float           share           = 0f;//將一個圓等分成n時每份所佔的度數
    private String          mTitleText      = null;
    private int             mTitleTextColor = 0;
    private int             mTitleTextSize  = 0;
    private Rect            mTextBound      = null;
    private Paint           mTextPaint      = null;
    private float           startPercent    = 0;//上一次的進度
    private int             flag            = 0;//標記是第一次設定percent的值
    private Drawable[]      drawablesCompleted = {
                            getResources().getDrawable(R.mipmap.yqcompleted),
                            getResources().getDrawable(R.mipmap.azcompleted),
                            getResources().getDrawable(R.mipmap.jdcjcompleted),
                            getResources().getDrawable(R.mipmap.sdcompleted),
                            getResources().getDrawable(R.mipmap.ntcompleted),
                            getResources().getDrawable(R.mipmap.mgwgcompleted)};
    private Drawable[]      drawablesNostart = {
                            getResources().getDrawable(R.mipmap.yqnostart),
                            getResources().getDrawable(R.mipmap.aznostart),
                            getResources().getDrawable(R.mipmap.jdcjnostart),
                            getResources().getDrawable(R.mipmap.sdnostart),
                            getResources().getDrawable(R.mipmap.ntnostart),
                            getResources().getDrawable(R.mipmap.mgwgnostart)};

    public ColorfulRingProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext       = context;
        TypedArray a        = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ColorfulRingProgressView, 0, 0);
        try {
            mBgColor        = a.getColor(R.styleable.ColorfulRingProgressView_bgColor, 0xffe1e1e1);
            mFgColorEnd     = a.getColor(R.styleable.ColorfulRingProgressView_fgColorEnd, 0xffff4800);
            mFgColorStart   = a.getColor(R.styleable.ColorfulRingProgressView_fgColorStart, 0xffffe400);
            mPercent        = a.getFloat(R.styleable.ColorfulRingProgressView_percent, 0);
            mStartAngle     = a.getFloat(R.styleable.ColorfulRingProgressView_startAngle, 0) + 270;
            mStrokeWidth    = a.getDimensionPixelSize(R.styleable.ColorfulRingProgressView_strokeWidth, dp2px(21));
            mTitleText      = a.getString(R.styleable.ColorfulRingProgressView_mTitleText);
            mTitleTextColor = a.getColor(R.styleable.ColorfulRingProgressView_mTitleTextColor, Color.RED);
            mTitleTextSize  = a.getDimensionPixelSize(R.styleable.ColorfulRingProgressView_mTitleTextSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
        } finally {
            a.recycle();
        }
        init();
    }

    private void init() {
        startPercent = mPercent;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        share = (float) (100) / divide;
        mTextBound = new Rect();
        mTextPaint = new Paint();
        mTextPaint.setTextSize(mTitleTextSize);
        mTextPaint.setColor(mTitleTextColor);
        // 計算描繪字型所需要的範圍
        mTextPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mTextBound);
    }

    private int dp2px(float dp) {
        return (int) (mContext.getResources().getDisplayMetrics().density * dp + 0.5f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int centerX = getWidth() / 2;// 獲得圓心的x座標
        int centerY = getHeight() / 2;// 獲得圓心的y座標
        /** step1、畫背景圈 */
        mPaint.setShader(null);
        mPaint.setColor(mBgColor);
        // 設定陰影 (柔邊, X軸位移, Y軸位移, 陰影顏色)
        mPaint.setShadowLayer(2, 3, 3, 0x8e1b1a);
        canvas.drawArc(mOval, 0, 360, false, mPaint);
        /** step2、畫進度圈 */
        mPaint.setShader(mShader);
        canvas.drawArc(mOval, mStartAngle, mPercent * 3.6f, false, mPaint);
        /** step3、畫百分比 */
        mPaint.setAntiAlias(true);// 消除鋸齒
        //計算文字的起始點
        //計算baseline(參考文獻) http://blog.csdn.net/sirnuo/article/details/21165665
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float descentY = centerY + fontMetrics.descent;
        // 設定字串的左邊在螢幕的中間
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        //根據要顯示的百分比與圓圈進度的百分比之比來換算要顯示的百分比
        canvas.drawText((int) (getPercent() * (Float.parseFloat(getmTitleText()) / startPercent)) + "", centerX, descentY, mTextPaint);//100
        /** step3、畫施工節點 */
        //這個地方寫的比較爛,暫時不知道有什麼更好的思路。
        for (int i = 0; i < mList.size(); i++) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesNostart[i]).getBitmap(), (float) mList.get(i).x - drawablesNostart[i].getIntrinsicWidth() * 0.5f, (float) mList.get(i).y - drawablesNostart[i].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        }
        if (getPercent() >= share * 0 && getPercent() < share * 1) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[2]).getBitmap(), (float) mList.get(2).x - drawablesCompleted[2].getIntrinsicWidth() * 0.5f, (float) mList.get(2).y - drawablesCompleted[0].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        } else if (getPercent() >= share * 1 && getPercent() < share * 2) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[2]).getBitmap(), (float) mList.get(2).x - drawablesCompleted[2].getIntrinsicWidth() * 0.5f, (float) mList.get(2).y - drawablesCompleted[0].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[3]).getBitmap(), (float) mList.get(3).x - drawablesCompleted[3].getIntrinsicWidth() * 0.5f, (float) mList.get(3).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        } else if (getPercent() >= share * 2 && getPercent() < share * 3) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[2]).getBitmap(), (float) mList.get(2).x - drawablesCompleted[2].getIntrinsicWidth() * 0.5f, (float) mList.get(2).y - drawablesCompleted[0].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[3]).getBitmap(), (float) mList.get(3).x - drawablesCompleted[3].getIntrinsicWidth() * 0.5f, (float) mList.get(3).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[4]).getBitmap(), (float) mList.get(4).x - drawablesCompleted[4].getIntrinsicWidth() * 0.5f, (float) mList.get(4).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        } else if (getPercent() >= share * 3 && getPercent() < share * 4) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[2]).getBitmap(), (float) mList.get(2).x - drawablesCompleted[2].getIntrinsicWidth() * 0.5f, (float) mList.get(2).y - drawablesCompleted[0].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[3]).getBitmap(), (float) mList.get(3).x - drawablesCompleted[3].getIntrinsicWidth() * 0.5f, (float) mList.get(3).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[4]).getBitmap(), (float) mList.get(4).x - drawablesCompleted[4].getIntrinsicWidth() * 0.5f, (float) mList.get(4).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[5]).getBitmap(), (float) mList.get(5).x - drawablesCompleted[5].getIntrinsicWidth() * 0.5f, (float) mList.get(5).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        } else if (getPercent() >= share * 4 && getPercent() < share * 5) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[2]).getBitmap(), (float) mList.get(2).x - drawablesCompleted[2].getIntrinsicWidth() * 0.5f, (float) mList.get(2).y - drawablesCompleted[0].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[3]).getBitmap(), (float) mList.get(3).x - drawablesCompleted[3].getIntrinsicWidth() * 0.5f, (float) mList.get(3).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[4]).getBitmap(), (float) mList.get(4).x - drawablesCompleted[4].getIntrinsicWidth() * 0.5f, (float) mList.get(4).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[5]).getBitmap(), (float) mList.get(5).x - drawablesCompleted[5].getIntrinsicWidth() * 0.5f, (float) mList.get(5).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[0]).getBitmap(), (float) mList.get(0).x - drawablesCompleted[0].getIntrinsicWidth() * 0.5f, (float) mList.get(0).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        } else if (getPercent() >= share * 5 && getPercent() <= share * 6) {
            canvas.save();
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[2]).getBitmap(), (float) mList.get(2).x - drawablesCompleted[2].getIntrinsicWidth() * 0.5f, (float) mList.get(2).y - drawablesCompleted[0].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[3]).getBitmap(), (float) mList.get(3).x - drawablesCompleted[3].getIntrinsicWidth() * 0.5f, (float) mList.get(3).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[4]).getBitmap(), (float) mList.get(4).x - drawablesCompleted[4].getIntrinsicWidth() * 0.5f, (float) mList.get(4).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[5]).getBitmap(), (float) mList.get(5).x - drawablesCompleted[5].getIntrinsicWidth() * 0.5f, (float) mList.get(5).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[0]).getBitmap(), (float) mList.get(0).x - drawablesCompleted[0].getIntrinsicWidth() * 0.5f, (float) mList.get(0).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.drawBitmap(((BitmapDrawable) drawablesCompleted[1]).getBitmap(), (float) mList.get(1).x - drawablesCompleted[1].getIntrinsicWidth() * 0.5f, (float) mList.get(1).y - drawablesCompleted[1].getIntrinsicHeight() * 0.5f, mPaint);
            canvas.restore();
        }
    }

    //求一個圓劃分成n等份,r是半徑,(x,y)為圓心座標的所有座標值
    private ArrayList<XYZ> onCoordinatePoints(int n, double r, double x, double y) {
        ArrayList<XYZ> mXYZs = new ArrayList<XYZ>();
        // n為分成了多少份
        for (int i = 1; i <= n; i++) {
            double rad = (2 * Math.PI / n) * (double) i;
            rad += Math.PI / 2f;
            double mx = r * Math.cos(rad);
            double my = r * Math.sin(rad);
            //加上圓心座標
            mXYZs.add(new XYZ(mx + x, my + y));
        }
        return mXYZs;
    }

    class XYZ {
        public double x;
        public double y;

        public XYZ(double x, double y) {
            this.x = x;
            this.y = y;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        updateOval();

        mShader = new LinearGradient(mOval.left, mOval.top,
                mOval.left, mOval.bottom, mFgColorStart, mFgColorEnd, Shader.TileMode.MIRROR);
    }

    public float getPercent() {
        return mPercent;
    }

    public void setPercent(float mPercent) {
        this.mPercent = mPercent;
        if (flag == 0) {
            flag++;
            startPercent = mPercent;
        }
        refreshTheLayout();
    }

    public String getmTitleText() {
        return mTitleText;
    }

    public void setmTitleText(String mTitleText) {
        this.mTitleText = mTitleText;
        refreshTheLayout();
    }

    public float getStrokeWidth() {
        return mStrokeWidth;
    }

    public void setStrokeWidth(float mStrokeWidth) {
        this.mStrokeWidth = mStrokeWidth;
        mPaint.setStrokeWidth(mStrokeWidth);
        updateOval();
        refreshTheLayout();
    }

    private void updateOval() {
        //修改:增加50距離,來畫外圈節點
        int xp = getPaddingLeft() + distance + getPaddingRight() + distance;
        int yp = getPaddingBottom() + distance + getPaddingTop() + distance;
        mOval = new RectF(getPaddingLeft() + distance + mStrokeWidth, getPaddingTop() + distance + mStrokeWidth,
                getPaddingLeft() + distance + (getWidth() - xp) - mStrokeWidth,
                getPaddingTop() + distance + (getHeight() - yp) - mStrokeWidth);
        //計算r
        r = (mOval.right - mOval.left) / 2;
        //增加半徑長度,畫節點
        mList = onCoordinatePoints(divide, r + stage, r + mOval.left, r + mOval.top);
    }

    public void setStrokeWidthDp(float dp) {
        this.mStrokeWidth = dp2px(dp);
        mPaint.setStrokeWidth(mStrokeWidth);
        updateOval();
        refreshTheLayout();
    }

    public void refreshTheLayout() {
        invalidate();
        requestLayout();
    }

    public int getFgColorStart() {
        return mFgColorStart;
    }

    public void setFgColorStart(int mFgColorStart) {
        this.mFgColorStart = mFgColorStart;
        mShader = new LinearGradient(mOval.left, mOval.top,
                mOval.left, mOval.bottom, mFgColorStart, mFgColorEnd, Shader.TileMode.MIRROR);
        refreshTheLayout();
    }

    public int getFgColorEnd() {
        return mFgColorEnd;
    }

    public void setFgColorEnd(int mFgColorEnd) {
        this.mFgColorEnd = mFgColorEnd;
        mShader = new LinearGradient(mOval.left, mOval.top,
                mOval.left, mOval.bottom, mFgColorStart, mFgColorEnd, Shader.TileMode.MIRROR);
        refreshTheLayout();
    }


    public float getStartAngle() {
        return mStartAngle;
    }

    public void setStartAngle(float mStartAngle) {
        this.mStartAngle = mStartAngle + 270;
        refreshTheLayout();
    }
}
2.MainActivity
package com.yx.yxcustomprogress;

import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.animation.LinearInterpolator;

public class MainActivity extends Activity {

    private ColorfulRingProgressView project_schedul_crpv = null;
    private ObjectAnimator anim = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        project_schedul_crpv = (ColorfulRingProgressView) findViewById(R.id.project_schedul_crpv);
        project_schedul_crpv.setPercent(70);//設定進度條的進度,最大值為100
        project_schedul_crpv.setmTitleText("60");//設定百分比,最大值為100
        anim = ObjectAnimator.ofFloat(project_schedul_crpv, "percent", 0, (project_schedul_crpv).getPercent());//設定動畫
        anim.setInterpolator(new LinearInterpolator());
        anim.setDuration(2000);
        anim.start();

        project_schedul_crpv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                anim.start();
            }
        });
    }
}
3.效果圖