Android -- ProcessBar 圆形进度条

Android – ProcessBar 圆形进度条

效果图:
在这里插入图片描述

实现原理: 以底部空心圆为进度条背景,在其上用相同属性画笔画弧度作为有效进度,文字则置于该圆圆心处;画笔粗细则控制进度条宽度。

使用方式:

private VirgoCircleProcessBar virgoCircleProcessBar;
private ProgressBar progressBar;
private SeekBar seekBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
	
    virgoCircleProcessBar = findViewById(R.id.circleBar);
	progressBar = findViewById(R.id.processBar);
	seekBar = findViewById(R.id.seekBar);

    seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    Log.i(TAG, "onProgressChanged: " + progress);
                    progressBar.setProgress(progress);
                    //设置当前进度
                    virgoCircleProcessBar.refreshProcess(progress);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {

                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {

                }
            });
    
}

布局:

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

    <!--含自定义属性-->
    <com.nepalese.virgomusic.presentation.component.VirgoCircleProcessBar
        android:id="@+id/circleBar"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:cbBackColor="@color/black"
        app:cbFrontColor="@color/colorGreen"
        app:cbTextColor="@color/colorRed"
        app:cbStrokeWidth="10dp"
        app:cbRadius="100dp"
        app:cbTextSize="25sp"
        app:cbProcess="0"
        app:cbMax="100"
        app:cbAddText="true"/>

    <ProgressBar
        android:id="@+id/processBar"
        android:layout_below="@id/circleBar"
        android:layout_margin="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_alignParentBottom="true"
        android:layout_margin="@dimen/margin_20"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</RelativeLayout>

属性文件:(attrs.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleProcessBar">
        <attr name="cbBackColor" format="color|reference" />
        <attr name="cbFrontColor" format="color|reference" />
        <attr name="cbTextColor" format="color|reference" />
        <attr name="cbTextSize" format="dimension|reference" />
        <attr name="cbRadius" format="dimension|reference" />
        <attr name="cbStrokeWidth" format="dimension|reference" />
        <attr name="cbProcess" format="integer" />
        <attr name="cbMax" format="integer" />
        <attr name="cbAddText" format="boolean"/>
    </declare-styleable>
</resources>

码源:

package com.nepalese.virgomusic.presentation.component;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import com.nepalese.virgomusic.R;

/**
 * @author nepalese on 2020/12/4 15:21
 * @usage
 */
public class VirgoCircleProcessBar extends View {
    private static final String TAG = "CircleProcessBar";

    private Context context;

    private Paint backPaint;//背景
    private Paint frontPaint;//前景
    private Paint textPaint;//文字
    private int backColor, frontColor, textColor;
    private float textSize;//字体大小
    private float textHeight;//文字高度,用来调整位置

    private float strokeWidth;//画笔粗细,控制进度条粗细

    private float radius;//半径,控制控件大小(w, h自适应)
    private RectF rect;

    private int progress;//初始值
    private int max;//最大值

    private int width;
    private int height;

    private boolean addText;//是否需要中间文字

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

    public VirgoCircleProcessBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VirgoCircleProcessBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    //完成相关参数初始化
    private void init(Context context, AttributeSet attrs) {
        this.context = context;
        //解析自定义属性
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleProcessBar);
        backColor = ta.getColor(R.styleable.CircleProcessBar_cbBackColor, 0xffffffff);
        frontColor = ta.getColor(R.styleable.CircleProcessBar_cbFrontColor, 0xff66ccff);
        textColor = ta.getColor(R.styleable.CircleProcessBar_cbTextColor, 0xffff0000);
        textSize = ta.getDimension(R.styleable.CircleProcessBar_cbTextSize, 25f);

        strokeWidth = ta.getDimension(R.styleable.CircleProcessBar_cbStrokeWidth, 50f);
        radius = ta.getDimension(R.styleable.CircleProcessBar_cbRadius, 200f);

        progress = ta.getInt(R.styleable.CircleProcessBar_cbProcess, 0);
        max = ta.getInt(R.styleable.CircleProcessBar_cbMax, 100);
        addText = ta.getBoolean(R.styleable.CircleProcessBar_cbAddText, true);
        ta.recycle();
        // </end>

        // 初始化paint
        backPaint = new Paint();
        backPaint.setColor(backColor);
        backPaint.setAntiAlias(true);
        backPaint.setStyle(Paint.Style.STROKE);
        backPaint.setStrokeWidth(strokeWidth);

        frontPaint = new Paint();
        frontPaint.setColor(frontColor);
        frontPaint.setAntiAlias(true);
        frontPaint.setStyle(Paint.Style.STROKE);
        frontPaint.setStrokeWidth(strokeWidth);

        textPaint = new Paint();
        textPaint.setColor(textColor);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);
        textPaint.setTextAlign(Paint.Align.CENTER);

        Paint.FontMetrics fm = textPaint.getFontMetrics();
        textHeight = (fm.descent - fm.ascent) / 3 * 2;//调整文字使其居中
    }


    //重写测量大小的onMeasure方法和绘制View的核心方法onDraw()
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getRealSize(widthMeasureSpec);
        height = getRealSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        initRect();//根据半径找到外切正方形
        float angle = progress / (float) max * 360;//获取角度比例

        //先在底部画一个圆(空心圆)
        canvas.drawCircle(width / 2f, height / 2f, radius, backPaint);
        //画弧度
        canvas.drawArc(rect, -90, angle, false, frontPaint);
        //文字
        if(addText){
            canvas.drawText(progress + "%", width / 2f, (height+textHeight) / 2f, textPaint);
        }
    }

    private void initRect() {
        if (rect == null) {
            rect = new RectF();
            int viewSize = (int) (radius * 2);
            int left = (width - viewSize) / 2;
            int top = (height - viewSize) / 2;
            int right = left + viewSize;
            int bottom = top + viewSize;
            rect.set(left, top, right, bottom);
        }
    }

    public int getRealSize(int measureSpec) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED) {
            result = (int) (radius * 2 + strokeWidth);
        } else {
            result = size;
        }
        return result;
    }

    //外部调用接口:初始化
    public void setAddText(boolean addText) {
        this.addText = addText;
    }

    public void setBackColor(int backColor) {
        this.backColor = backColor;
    }

    public void setFrontColor(int frontColor) {
        this.frontColor = frontColor;
    }

    public void setTextColor(int textColor) {
        this.textColor = textColor;
    }

    public void setTextSize(float textSize) {
        this.textSize = textSize;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public void setMax(int max) {
        this.max = max;
    }

    //初始化后修改进度
    public void refreshProcess(int process){
        this.progress = process;
        invalidate();
    }
}
 = radius;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public void setMax(int max) {
        this.max = max;
    }

    //初始化后修改进度
    public void refreshProcess(int process){
        this.progress = process;
        invalidate();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值