自定义进度条

设计图.png

项目需要以上设计图中进度条样式,百度了下没有找到相同的,只能自己写一个。先上完成的效果图
截屏1.png

截屏2.png

截屏3.png

截屏4.png

上代码

package com.wsf.myprogressbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.text.Layout;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.view.View;


/**
 * 进度条上方文字view
 *
 * @author wsf
 */
public class TextProgressView extends View {

    private Paint paint;
    private float percent;
    private float total;

    /**
     * 进度条文字颜色
     */
    private int textColor;
    /**
     * 进度条数字颜色
     */
    private int numColor;

    /**
     * 进度条数字
     */
    private int number;

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

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

    public TextProgressView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint();
        paint.setAntiAlias(true);
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
                R.styleable.TextProgressView);
        //获取自定义属性和默认值
        textColor = mTypedArray.getColor(R.styleable.TextProgressView_textColor, getResources().getColor(R.color.thimcolor));
        numColor = mTypedArray.getColor(R.styleable.TextProgressView_numColor, getResources().getColor(R.color.color_ffb202));
        mTypedArray.recycle();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(getResources().getColor(R.color.thimcolor));// 设置累计颜色
        //用SpannableString设置字体不同颜色大小
        SpannableString spannableString = new SpannableString("剩余天数: " + getNumber() + "天");
        spannableString.setSpan(new ForegroundColorSpan(numColor), 5, spannableString.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        spannableString.setSpan(new AbsoluteSizeSpan(sp2px(16)), 5, spannableString.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        paint.setColor(textColor);// 设置累计颜色
        paint.setTextSize(sp2px(16));
        //计算文本宽高
        Rect rect = new Rect();
        String text = "剩余天数: " + getNumber() + "天";
        paint.getTextBounds(text, 0, text.length(), rect);
        float textWidth = rect.width();
        float textHeight = rect.height() + (2 * sp2px(4));
        paint.setStrokeWidth(sp2px(1));
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        Path path = new Path();
        //先画文本框
        if (total != 0) {
            if (percent == 0) { //如果进度为0,则从0,0开始画
                path.moveTo(0, 0);
                path.lineTo(0, textHeight);
                path.lineTo((getWidth() * percent / total), getHeight());
                path.lineTo((getWidth() * percent / total) + sp2px(5), textHeight);
                path.lineTo(textWidth, textHeight);
                path.lineTo(textWidth, 0);
                path.close();
            } else if (percent == total) {//如果进度等于总和,则文本框右侧贴着画布右侧
                path.moveTo(getWidth() - textWidth, 0);
                path.lineTo(getWidth() - textWidth, textHeight);
                path.lineTo((getWidth() * percent / total) - sp2px(5), textHeight);
                path.lineTo((getWidth() * percent / total), getHeight());
                path.lineTo(getWidth(), textHeight);
                path.lineTo(getWidth(), 0);
                path.close();
            } else {//其他情况按正常画
                path.moveTo((getWidth() * percent / total) - (textWidth * percent / total), 0);
                path.lineTo((getWidth() * percent / total) - (textWidth * percent / total), textHeight);
                if ((getWidth() * percent / total) - sp2px(5) < 0) {
                    path.lineTo(0, textHeight);
                } else {
                    path.lineTo((getWidth() * percent / total) - sp2px(5), textHeight);
                }
                path.lineTo((getWidth() * percent / total), getHeight());
                if ((getWidth() * percent / total) + sp2px(5) > getWidth()) {
                    path.lineTo(getWidth(), textHeight);
                } else {
                    path.lineTo((getWidth() * percent / total) + sp2px(5), textHeight);
                }
                path.lineTo((getWidth() * percent / total) + (textWidth * (total - percent) / total), textHeight);
                path.lineTo((getWidth() * percent / total) + (textWidth * (total - percent) / total), 0);
                path.close();
            }
        } else {//如果总和为0,直接在最右端画文本框
            path.moveTo(getWidth() - textWidth, 0);
            path.lineTo(getWidth() - textWidth, textHeight);
            path.lineTo((getWidth() * percent / total) - sp2px(5), textHeight);
            path.lineTo((getWidth() * percent / total), getHeight());
            if ((getWidth() * percent / total) + sp2px(5) > getWidth()) {
                path.lineTo(getWidth(), textHeight);
            } else {
                path.lineTo((getWidth() * percent / total) + sp2px(5), textHeight);
                path.lineTo(getWidth(), textHeight);
            }
            path.lineTo(getWidth(), 0);
            path.close();
        }
        canvas.drawPath(path, paint);
        //画文本
        TextPaint paint1 = new TextPaint();
        paint1.setTextSize(sp2px(12));
        paint1.setColor(textColor);
        paint1.setAntiAlias(true);
        StaticLayout layout = new StaticLayout(spannableString, paint1, getWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
        canvas.save();
        if (total != 0) {
            if (percent == 0){
                canvas.translate(0 + sp2px(10), 0); //不知道为什么文字的画布位置靠左,所以加10是为了文字可以显示到文字框的中间
            } else if (percent == total) {
                canvas.translate(getWidth() - textWidth + sp2px(10), 0);
            } else {
                canvas.translate((getWidth() * percent / total) - (textWidth * percent / total) + sp2px(10), 0);
            }
        } else {
            canvas.translate(getWidth() - textWidth + sp2px(10), 0);
        }
        layout.draw(canvas);
        canvas.restore();
    }

    public float getPercent() {
        return percent;
    }

    public void setPercent(float percent) {
        this.percent = percent;
    }

    public float getTotal() {
        return total;
    }

    public void setTotal(float total) {
        this.total = total;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    private int dp2px(int value) {
        float v = getContext().getResources().getDisplayMetrics().density;
        return (int) (v * value + 0.5f);
    }

    private int sp2px(int value) {
        float v = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (v * value + 0.5f);
    }

}

package com.wsf.myprogressbar;


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



/**
 * 进度条
 *
 * @author wsf
 */
public class DayProgressView extends View {
    /**
     * 画笔对象的引用
     */
    private Paint paint;

    /**
     * 进度
     */
    private float mRate;
    /**
     * 总共
     */

    private float total;
    /**
     * 进度条左侧颜色
     */
    private int leftColor;
    /**
     * 进度条右侧颜色
     */
    private int rightColor;
    /**
     * 进度条右侧颜色
     */
    private int defaultColor;
    /**
     * 进度条左右两端弧度
     */
    private float mRadius;
    private Path mPath;
    private Path mPath1;
    private Path mPath2;


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

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

    public DayProgressView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint();
        paint.setAntiAlias(true);
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
                R.styleable.DayProgressView);
//		获取自定义属性和默认值
        mRadius = mTypedArray.getDimension(R.styleable.DayProgressView_rectRadius, dp2px(10));
        leftColor = mTypedArray.getColor(R.styleable.DayProgressView_leftColor, getResources().getColor(R.color.thimcolor));
        rightColor = mTypedArray.getColor(R.styleable.DayProgressView_rightColor, getResources().getColor(R.color.color_ffb202));
        defaultColor = mTypedArray.getColor(R.styleable.DayProgressView_defaultColor, getResources().getColor(R.color.white));
        mTypedArray.recycle();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //先画背景色
        paint.setColor(defaultColor);// 设置累计颜色
        paint.setStyle(Paint.Style.FILL);//设置填满
        mPath2 = new Path();
        mPath2.moveTo(0, 0);
        //画白色背景图
        RectF RoundRect = new RectF(0, 0, getHeights(canvas), getHeights(canvas));
        //左端画弧形,从270的角度开始逆时针画180度
        mPath2.arcTo(RoundRect, 270, -180);
        mPath2.lineTo(getWidths(canvas), getHeights(canvas));
        RectF RoundRect1 = new RectF(getWidths(canvas) - getHeights(canvas), 0, getWidths(canvas), getHeights(canvas));
        //右端画弧形,从90的角度开始逆时针画180度
        mPath2.arcTo(RoundRect1, 90, -180);
        mPath2.close();
        canvas.drawPath(mPath2,paint);

        if (mRate == total){//当前进度等于全部时,代表全部完成,左侧颜色填充满
            paint.setColor(leftColor);// 蓝色
            paint.setStyle(Paint.Style.FILL);
            canvas.drawPath(mPath2,paint);
        }else if (mRate == 0){//当前进度等于0时,代表位开始,右侧颜色填充满
            paint.setColor(rightColor);// 橙色
            paint.setStyle(Paint.Style.FILL);
            canvas.drawPath(mPath2,paint);
        }else {
            //画左侧进度条
            paint.setColor(leftColor);// 蓝色
            paint.setStyle(Paint.Style.FILL);
            mPath = new Path();
            //从0,0位置开始画
            mPath.moveTo(0, 0);
            //画左端半圆,直径是画布的高
            RectF rectF = new RectF(0, 0, getHeights(canvas), getHeights(canvas));
            //左端画弧形,从270的角度开始逆时针画180度
            mPath.arcTo(rectF, 270, -180);
            //左右两种颜色中间间隙是  getHeights(canvas) / 4
            if (getWidths(canvas) * mRate / total < getHeights(canvas)/2){//当进度条的左侧颜色小于左端半圆宽度时,默认是至少显示左侧半圆
                mPath.lineTo(getHeights(canvas)/2 + getHeights(canvas) / 4, 0);
            }else if (getWidths(canvas) - getHeights(canvas)/2 < getWidths(canvas) * mRate / total){//当进度条的右侧颜色小于右端半圆宽度时,默认是至少显示右侧半圆
                mPath.lineTo(getWidths(canvas) - getHeights(canvas) / 2 - getHeights(canvas) / 4 * 2, getHeights(canvas));
                mPath.lineTo(getWidths(canvas) - getHeights(canvas) / 2 - getHeights(canvas) / 4, 0);
            }else {//其他情况按正常进度画
                mPath.lineTo((getWidths(canvas)) * mRate / total - getHeights(canvas) / 4, getHeights(canvas));
                mPath.lineTo((getWidths(canvas)) * mRate / total, 0);
            }
            mPath.close();
            canvas.drawPath(mPath, paint);
            //画右侧进度条
            paint.setColor(rightColor);
            paint.setStyle(Paint.Style.FILL);
            mPath1 = new Path();
            //右侧判断同左侧判断
            if (getWidths(canvas) * mRate / total < getHeights(canvas)/2){
                mPath1.moveTo(getHeights(canvas)/2 + getHeights(canvas) / 4 + getHeights(canvas) / 4, 0);
                mPath1.lineTo(getHeights(canvas)/2 + getHeights(canvas) / 4, getHeights(canvas));
            }else if (getWidths(canvas) - getHeights(canvas)/2 < getWidths(canvas) * mRate / total){
                mPath1.moveTo(getWidths(canvas) - getHeights(canvas) / 2, 0);
                mPath1.lineTo(getWidths(canvas) - getHeights(canvas) / 2 - getHeights(canvas) / 4, getHeights(canvas));
            }else {
                mPath1.moveTo(getWidths(canvas) * mRate / total + getHeights(canvas) / 4, 0);
                mPath1.lineTo(getWidths(canvas) * mRate / total, getHeights(canvas));
            }
            mPath1.lineTo(getWidths(canvas), getHeights(canvas));
            RectF rectF1 = new RectF(getWidths(canvas) - getHeights(canvas), 0, getWidths(canvas), getHeights(canvas));
            //右端画弧形,从90的角度开始逆时针画180度
            mPath1.arcTo(rectF1, 90, -180);
            mPath1.close();
            canvas.drawPath(mPath1, paint);
        }
    }

    public float getRate() {
        return mRate;
    }

    public void setRate(float mRate) {
        this.mRate = mRate;
    }

    public int getLeftColor() {
        return leftColor;
    }

    public void setLeftColor(int leftColor) {
        this.leftColor = leftColor;
    }

    public int getRightColor() {
        return rightColor;
    }

    public void setRightColor(int rightColor) {
        this.rightColor = rightColor;
    }

    public float getRadius() {
        return mRadius;
    }

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

    public int getDefaultColor() {
        return defaultColor;
    }

    public void setDefaultColor(int defaultColor) {
        this.defaultColor = defaultColor;
    }

    public float getTotal() {
        return total;
    }

    public void setTotal(float total) {
        this.total = total;
    }

    private float getWidths(Canvas canvas) {
        return (float) canvas.getWidth();
    }

    private float getHeights(Canvas canvas) {
        return (float) canvas.getHeight();
    }

    private int sp2px(int value) {
        float v = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (v * value + 0.5f);
    }
    private int dp2px(int value) {
        float v = getContext().getResources().getDisplayMetrics().density;
        return (int) (v * value + 0.5f);
    }
}

这里是将下方的进度条和上方显示的文字分开写了,因为项目时间紧张,合在一起计算太麻烦,分开写好计算,等以后有时间再合并,因为是分开显示并且左右两端是半圆形中间有斜着的分隔,所以在左右两端文本框箭头指示位置有问题,不过影响不大,除了合并,暂时没想到好的办法,有哪位朋友有方法可以评论告诉我,十分感谢!

package com.wsf.myprogressbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private EditText et_total;
    private EditText et_rate;
    private Button btn_refresh;
    private DayProgressView dpvClassdetail;
    private TextProgressView tpvClassdetail;
    private TextView tvClassDetailStarttime;
    private TextView tvClassDetailEndtime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_total = findViewById(R.id.et_total);
        et_rate = findViewById(R.id.et_rate);
        btn_refresh = findViewById(R.id.btn_refresh);
        dpvClassdetail = findViewById(R.id.dpv_classdetail);
        tpvClassdetail = findViewById(R.id.tpv_classdetail);
        tvClassDetailStarttime = findViewById(R.id.tv_class_detail_starttime);
        tvClassDetailEndtime = findViewById(R.id.tv_class_detail_endtime);
        et_total.setText(10 + "");
        et_rate.setText(3 + "");
        refresh();
        btn_refresh.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                refresh();
            }
        });
    }

    private void refresh() {
        if (!TextUtils.isEmpty(et_total.getText().toString()) && !TextUtils.isEmpty(et_rate.getText().toString())) {
            int total = Integer.parseInt(et_total.getText().toString());
            int rate = Integer.parseInt(et_rate.getText().toString());
            dpvClassdetail.setTotal(total);
            if (total - rate < 0) {
                dpvClassdetail.setRate(0);
                tpvClassdetail.setPercent(0);
            } else {
                dpvClassdetail.setRate(rate);
                tpvClassdetail.setPercent(rate);
            }
            tpvClassdetail.setNumber(total - rate);
            tpvClassdetail.setTotal(total);
            tpvClassdetail.postInvalidate();
            dpvClassdetail.postInvalidate();
            tvClassDetailStarttime.setText("开始时间 \n" + "2020.4.20");
            tvClassDetailEndtime.setText("截止时间 \n" + "2020.5.20");
        } else {
            Toast.makeText(MainActivity.this, "请填写总和与进度", Toast.LENGTH_LONG).show();
        }
    }
}

这里是mainActivity中使用

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical">


        <EditText
            android:id="@+id/et_total"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:hint="请填写总和"
            android:padding="5dp" />

        <EditText
            android:id="@+id/et_rate"
            android:layout_marginTop="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:hint="请填写进度"
            android:padding="5dp" />

        <Button
            android:id="@+id/btn_refresh"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:padding="5dp"
            android:text="刷新" />

    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:orientation="horizontal"
        android:paddingLeft="5dp"
        android:paddingTop="15dp"
        android:paddingRight="5dp"
        android:paddingBottom="15dp">

        <TextView
            android:id="@+id/tv_class_detail_starttime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:gravity="center"
            android:text=""
            android:textColor="#666666"
            android:textSize="12sp" />

        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:layout_marginBottom="10dp"
            android:layout_weight="1">

            <com.wsf.myprogressbar.DayProgressView
                android:id="@+id/dpv_classdetail"
                android:layout_width="match_parent"
                android:layout_height="11dp"
                android:layout_alignParentBottom="true" />

            <com.wsf.myprogressbar.TextProgressView
                android:id="@+id/tpv_classdetail"
                android:layout_width="match_parent"
                android:layout_height="33dp" />
        </RelativeLayout>

        <TextView
            android:id="@+id/tv_class_detail_endtime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:gravity="center"
            android:text=""
            android:textColor="#666666"
            android:textSize="12sp" />
    </LinearLayout>

</LinearLayout>

demo中xml文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值