文章目录

  • 1、简介
  • 2、步骤
  • 2.1 先添加自定义的属性 attrs.xml
  • 2.2 、编写自定义的 progress view CustomizeProgressBarView.java
  • 2.3、布局文件 activity_main.xml
  • 2.4、主文件 MainActivity .java


1、简介

就是自定义一个圆形进度条

ProgressBar 圆形进度条_xml

2、步骤

文件结构:

ProgressBar 圆形进度条_android_02

2.1 先添加自定义的属性 attrs.xml

先在res\values目录下创建attrs.xml

ProgressBar 圆形进度条_android_03

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircularProgressView">
        <attr name="ringWidth" format="dimension" />
        <attr name="ringColor" format="color|reference" />
        <attr name="progressTitle" format="string" />
    </declare-styleable>
</resources>
2.2 、编写自定义的 progress view CustomizeProgressBarView.java

CustomizeProgressBarView.java

package com.example.myapplication;


import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
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.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

/**
 * Circular progress view
 */
public class CustomizeProgressBarView extends View {
    private String TAG = "CustomizeProgressBarView: ";
    /**
     * 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;

    private AnimatorSet animation = null;

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

    public CustomizeProgressBarView(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.BLUE);
        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.
     */
    public void setProgress(float progress) {
        this.progress = PROGRESS_FACTOR * progress / 100.0f;
        Log.i(TAG,"progress " + this.progress);
        if (this.progress < -360) {
            this.progress = -360;
        }
        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.
     */
    @SuppressLint("WrongConstant")
    public void startAnim(float progress, boolean isAnim) {
         animation = new AnimatorSet();

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

        progressAnimation.setRepeatCount(ValueAnimator.INFINITE);//无限循环
        progressAnimation.setRepeatMode(ValueAnimator.INFINITE);

        //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();
    }

    public  void stopAnimation() {
        Log.i(TAG,"stop animation");
        animation.end();
    }

}
2.3、布局文件 activity_main.xml

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <com.example.myapplication.CustomizeProgressBarView
        android:layout_width="350dp"
        android:layout_height="350dp"
        android:id="@+id/progress_bar_id"
        app:ringWidth = "20dp"
        app:ringColor = "@color/colorAccent"
        app:progressTitle ="Bar"/>


    <Button
        android:layout_width="300dp"
        android:layout_height="50dp"
        android:id="@+id/add_but_id"
        android:text="add progress"/>


    <Button
        android:layout_width="300dp"
        android:layout_height="50dp"
        android:id="@+id/start_but_id"
        android:text="start animation"/>


    <Button
        android:layout_width="300dp"
        android:layout_height="50dp"
        android:id="@+id/stop_but_id"
        android:text="stop animation"/>


</LinearLayout>
2.4、主文件 MainActivity .java

MainActivity .java

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private  String TAG  = "MainActivity: ";
    private Button mButtonAdd;
    private Button mButtonStart;
    private Button mButtonStop;

    private float progress = 0f;
    private CustomizeProgressBarView mCustomizeProgressBarView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCustomizeProgressBarView = findViewById(R.id.progress_bar_id);

        mButtonAdd = findViewById(R.id.add_but_id);
        mButtonStart = findViewById(R.id.start_but_id);
        mButtonStop = findViewById(R.id.stop_but_id);


        mButtonAdd.setOnClickListener(this);
        mButtonStart.setOnClickListener(this);
        mButtonStop.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.add_but_id:
                progress = progress + 10f;
                if (progress >= 100) {
                    progress = 0;
                }
                mCustomizeProgressBarView.setProgress(progress);
                Log.i(TAG,"set progress " + progress);
                break;
            case R.id.start_but_id:
                mCustomizeProgressBarView.startAnim(100f,true);
                Log.i(TAG,"start animation");
                break;
            case R.id.stop_but_id:
                mCustomizeProgressBarView.stopAnimation();
                Log.i(TAG,"stop animation");
                break;
        }
    }
}