美文网首页
自定义进度条

自定义进度条

作者: CoolKin | 来源:发表于2020-04-29 16:13 被阅读0次
    设计图.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文件

    相关文章

      网友评论

          本文标题:自定义进度条

          本文链接:https://www.haomeiwen.com/subject/yqqnwhtx.html