1. 程式人生 > >一款不錯的Android環形進度條

一款不錯的Android環形進度條

      現在市面上有很多app都使用了環形進度條,可以反映當前使用者的一個進度狀態,今天給大家帶來一款環形的,帶動畫和數字提示的進度條,拋磚引玉,希望大家喜歡。話不多說,先上簡單的效果圖:


      可以設定動畫,它的進度條和數字提示會隨著動畫進行而變化。以下是具體程式碼:

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
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.support.annotation.Keep;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import your project.R;

/**
 * Circular progress view
 */
public class CircularProgressView extends View {
    /**
     * Factor to convert the factor to paint the arc.
     * <p/>
     * In this way the developer can use a more user-friendly [0..1f] progress
     */
    public static final int PROGRESS_FACTOR = -360;
    /**
     * Property Progress of the outer circle.
     * <p/>
     * The progress of the circle. If  is set
     * to FALSE, this property will be used to indicate the completion of the outer circle [0..1f].
     * <p/>
     * If set to TRUE, the drawable will activate the loading mode, where the drawable will
     * show a 90º arc which will be spinning around the outer circle as much as progress goes.
     */
    public static final String PROGRESS_PROPERTY = "progress";
    /**
     * Rectangle where the filling ring will be drawn into.
     */
    protected final RectF arcElements;
    /**
     * Width of the filling ring.
     */
    protected int ringWidth;
    /**
     * Paint object to draw the element.
     */
    protected final Paint paint;
    /**
     * Ring progress.
     */
    protected float progress;
    /**
     * Color for the completed ring.
     */
    protected int ringColor;
    /**
     * Ring progress title.
     */
    protected String progressTitle;
    /**
     * default gradient color for the progress ring.
     */
    private LinearGradient shader;
    private Rect rec;

    public CircularProgressView(Context context) {
        this(context, null);
    }

    public CircularProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CircularProgressView);
        ringColor = array.getColor(R.styleable.CircularProgressView_ringColor, 0);
        ringWidth = (int) array.getDimension(R.styleable.CircularProgressView_ringWidth, 20);
        progressTitle = array.getString(R.styleable.CircularProgressView_progressTitle);
        array.recycle();
        this.progress = 0;
        this.paint = new Paint();
        this.paint.setAntiAlias(true);
        this.arcElements = new RectF();
        rec = new Rect();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // Calculations on the different components sizes
        int size = Math.min(canvas.getHeight(), canvas.getWidth());
        float outerRadius = (size / 2) - (ringWidth / 2);
        float offsetX = (canvas.getWidth() - outerRadius * 2) / 2;
        float offsetY = (canvas.getHeight() - outerRadius * 2) / 2;

        int halfRingWidth = ringWidth / 2;
        float arcX0 = offsetX + halfRingWidth;
        float arcY0 = offsetY + halfRingWidth;
        float arcX = offsetX + outerRadius * 2 - halfRingWidth;
        float arcY = offsetY + outerRadius * 2 - halfRingWidth;

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(ringWidth);
        paint.setStrokeCap(Paint.Cap.ROUND);
        arcElements.set(arcX0, arcY0, arcX, arcY);
        paint.setColor(Color.GRAY);
        canvas.drawArc(arcElements, 0, 360, false, paint);
        if (ringColor != 0) {
            paint.setColor(ringColor);
            canvas.drawArc(arcElements, -90, -progress, false, paint);
        } else {
            if (shader == null) {
                shader = new LinearGradient(0, offsetY, 0, offsetY + outerRadius * 2, new int[]{Color.parseColor("#B4ED50"),
                        Color.parseColor("#429321")},
                        null, Shader.TileMode.CLAMP);
            }
            paint.setShader(shader);
            canvas.drawArc(arcElements, -90, -progress, false, paint);
        }

        int progressText = -(int) (progress / 3.6);
        String v = progressText + "%";
        paint.setShader(null);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);
        paint.setTextSize(spToPx(30));
        paint.getTextBounds(v, 0, v.length(), rec);
        int textwidth = rec.width();
        int textheight = rec.height();
        // draw the center words
        if (progressTitle != null && !progressTitle.isEmpty()) {
            canvas.drawText(v, (canvas.getWidth() - textwidth) / 2, (canvas.getHeight() + textheight) / 2 - dpToPx(20), paint);
            paint.setTextSize(spToPx(16));
            paint.getTextBounds(progressTitle, 0, progressTitle.length(), rec);
            int textwidth1 = rec.width();
            int textheight1 = rec.height();
            canvas.drawText(progressTitle, (canvas.getWidth() - textwidth1) / 2, (canvas.getHeight() + textheight1) / 2 + dpToPx(20), paint);
        } else {
            canvas.drawText(v, (canvas.getWidth() - textwidth) / 2, (canvas.getHeight() + textheight) / 2, paint);
        }
    }

    /**
     * Change sp to px.
     *
     * @param sp the sp value.
     * @return the px value.
     */
    private float spToPx(int sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getContext().getResources().getDisplayMetrics());
    }

    /**
     * Change dp to px.
     *
     * @param dp the dp value.
     * @return the px value.
     */
    private float dpToPx(int dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
    }

    /**
     * Returns the progress of the outer ring.
     * <p/>
     * Will output a correct value only when the indeterminate mode is set to FALSE.
     *
     * @return Progress of the outer ring.
     */
    public float getProgress() {
        return progress / PROGRESS_FACTOR;
    }

    /**
     * Sets the progress [0..1f].
     *
     * @param progress Sets the progress.
     */
    @Keep
    public void setProgress(float progress) {
        this.progress = PROGRESS_FACTOR * progress;
        invalidate();
    }

    /**
     * Gets the filled ring color.
     *
     * @return Returns the filled ring color.
     */
    public int getRingColor() {
        return ringColor;
    }

    /**
     * Sets the progress ring  color.
     *
     * @param ringColor Ring color in #AARRGGBB format.
     */
    public void setRingColor(int ringColor) {
        this.ringColor = ringColor;
        invalidate();
    }

    /**
     * Sets the ring width.
     *
     * @param ringWidth Default ring width.
     */
    public void setRingWidth(int ringWidth) {
        this.ringWidth = ringWidth;
        invalidate();
    }

    /**
     * Gets the ring width.
     *
     * @return Returns the ring width.
     */
    public int getRingWidth() {
        return ringWidth;
    }

    /**
     * Sets the progress title.
     *
     * @param progressTitle Sets the progress.
     */
    public void setProgressTitle(String progressTitle) {
        this.progressTitle = progressTitle;
        invalidate();
    }

    /**
     * Start progress animation or show the progress directly.
     *
     * @param progress the progress you set.
     * @param isAnim weather to show the animation.
     */
    public void startAnim(float progress, boolean isAnim) {
        AnimatorSet animation = new AnimatorSet();

        ObjectAnimator progressAnimation = ObjectAnimator.ofFloat(this, CircularProgressView.PROGRESS_PROPERTY,
                0f, progress);
        progressAnimation.setDuration(isAnim ? 1000 : 0);
        progressAnimation.setInterpolator(new AccelerateDecelerateInterpolator());

        //another kind of animation
//        ObjectAnimator colorAnimator = ObjectAnimator.ofInt(drawable, CircularProgressDrawable.RING_COLOR_PROPERTY,
//                getResources().getColor(android.R.color.holo_red_dark),
//                getResources().getColor(android.R.color.holo_green_light));
//        colorAnimator.setEvaluator(new ArgbEvaluator());
//        colorAnimator.setDuration(3600);
//        animation.playTogether(progressAnimation, colorAnimator);
        animation.play(progressAnimation);
        animation.start();
    }

}
      順帶需要加進arr.xml的自定義屬性:
<declare-styleable name="CircularProgressView">
        <attr name="ringWidth" format="dimension" />
        <attr name="ringColor" format="color|reference" />
        <attr name="progressTitle" format="string" />
</declare-styleable>
      咦,怎麼註釋全是英文?當然是為了提(bu)升(yao)逼(jie)格(yi)啦。由上可看出,可以在xml檔案中直接定義的屬性有ringWidth,即為圓環寬度;ringColor,為圓環顏色;progressTitle即為數字提示下面的文字提示,其餘屬性也可根據需求進行擴充套件。寬度可定義為march_parent,然後定義一個確切的高度,如效果圖即為180dp。用法很簡單,只需要在xml中定義上面的元件就可以了。當然,要讓它展示進度和動畫效果還必須呼叫startAnim(float progress, boolean isAnim)這個方法,傳入(0-100)的進度,第二個引數控制是否有動畫,不想展示動畫傳false就可以了。其他的我也不多說啦,請大家去程式碼裡面體會吧。