自定义进度条

概述

今天继续学习自定义View,今天的内容是自定义进度条,我们分为水平进度条和圆形进度条

水平进度条

先看效果图

这里写图片描述

首先我们要做到就是分析需要哪些自定义属性,然后我们在attrs文件中需要去声明这些属性,我们很容易分析出来有如下属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ProgressBarWithProgress">
        <!--未到达进度条颜色-->
        <attr name="progress_unreach_color" format="color"/>
        <!--未到达进度条高度-->
        <attr name="progress_unreach_height" format="dimension"/>
        <!--已到达进度条颜色-->
        <attr name="progress_reach_color" format="color"/>
        <!--已到达进度条高度-->
        <attr name="progress_reach_height" format="dimension"/>
        <!--进度文字颜色-->
        <attr name="progress_text_color" format="color"/>
        <!--进度文字大小-->
        <attr name="progress_text_size" format="dimension"/>
        <!--文字和进度条间距-->
        <attr name="progress_text_margin" format="dimension"/>
    </declare-styleable>

</resources>

分析完了所有的属性之后,我们开使绘制进度条,我们可以把进度条分为三段,已到达的进度条,文字,未到达的进度条。接下来我们在代码中分别进行绘制。

我们先来看一张图,
这里写图片描述

我们依据上面划分的原则来进行绘制

public class ProgressBarWithProgress extends ProgressBar {
    //默认属性的值
    private static final int DEFAULT_REACH_COLOR = 0XFF00FF;
    private static final int DEFAULT_REACH_HEIGHT = 5;
    private static final int DEFAULT_UNREACH_COLOR = 0XFF00;
    private static final int DEFAULT_UNREACH_HEIGHT = 4;
    private static final int DEFAULT_TEXT_COLOR = 0X00FF;
    private static final int DEFAULT_TEXT_SIZE = 10;
    private static final int DEFAULT_TEXT_MARGIN = 8;
    private int mReachHeight = dp2px(DEFAULT_REACH_HEIGHT);
    private int mUnReachHeight = dp2px(DEFAULT_UNREACH_HEIGHT);
    private int mTextSize = sp2px(DEFAULT_TEXT_SIZE);
    private int mTextMargin = dp2px(DEFAULT_TEXT_MARGIN);

    //实际的宽度,减去padding
    private int mReadWidth;
    //画笔
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final int reachColor;
    private final float reachHeight;
    private final int unReachColor;
    private final float unReachHeight;
    private final int textColor;
    private final float textSize;
    private final float textMargin;

    public ProgressBarWithProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        //将自定义的属性封装到typedArray中
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressBarWithProgress);
        reachColor = typedArray.getColor(R.styleable.ProgressBarWithProgress_progress_reach_color, DEFAULT_REACH_COLOR);
        reachHeight = typedArray.getDimension(R.styleable.ProgressBarWithProgress_progress_reach_height, mReachHeight);
        unReachColor = typedArray.getColor(R.styleable.ProgressBarWithProgress_progress_unreach_color, DEFAULT_UNREACH_COLOR);
        unReachHeight = typedArray.getDimension(R.styleable.ProgressBarWithProgress_progress_unreach_height, mUnReachHeight);
        textColor = typedArray.getColor(R.styleable.ProgressBarWithProgress_progress_text_color, DEFAULT_TEXT_COLOR);
        textSize = typedArray.getDimension(R.styleable.ProgressBarWithProgress_progress_text_size,mTextSize);
        textMargin = typedArray.getDimension(R.styleable.ProgressBarWithProgress_progress_text_margin,mTextMargin);
        typedArray.recycle();
        //设置画笔的所画文字大小,必须设置,否则无法获取文字高度
        mPaint.setTextSize(mTextSize);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = measureHeight(heightMeasureSpec);//进度条的高度
        int width = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(width,height);
        //获取实际的宽度,减去左右两边的间距
        mReadWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }

    @Override
    protected  void onDraw(Canvas canvas) {
        canvas.save();
        //平移画布,便于绘制
        canvas.translate(getPaddingLeft(),getHeight()/2);
        String text = getProgress() + "%";
        //是否需要绘制未到达的进度条
        boolean needDrawUnReach = true;
        //当前走过的进度比率
        float ratio = getProgress()*1.0f/getMax();
        //文字的宽度
        int textWidth = (int) mPaint.measureText(text);
        //当前进度所占总进度的宽度,这里我们以文字的结束位置为标准计算
        int progressX = (int) (ratio*mReadWidth);
        //已到达进度条结束点的坐标,我们这里的进度
        float endX = progressX-textMargin-textWidth;
        if(progressX>=mReadWidth){//progressX最大不能超过进度条宽度
            progressX = mReadWidth;
            needDrawUnReach = false;
        }
        if(endX > 0){
            mPaint.setColor(reachColor);
            mPaint.setStrokeWidth(reachHeight);
            //绘制已到达的进度条
            canvas.drawLine(0,0,endX,0,mPaint);
        }
        //画文字
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        //由于坐标移动到进度条的中间,二画文字的y坐标默认是文字的左下角,所以
        //y的坐标是文字高度的一半,并且是小于0
        int y = (int) (-(mPaint.descent() + mPaint.ascent())/2);
        canvas.drawText(text,progressX-textWidth,y,mPaint);
        //画未达到的进度条
        if(needDrawUnReach){
            Float start = progressX+textMargin;
            mPaint.setColor(unReachColor);
            mPaint.setStrokeWidth(unReachHeight);
            canvas.drawLine(start,0,mReadWidth,0,mPaint);
        }
        canvas.restore();//恢复画布
    }

    /**
     * 设置进度条的高度
     * @param heightMeasureSpec
     * @return
     */
    public int measureHeight(int heightMeasureSpec){
        int result = 0;
        int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);
        if(specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else{
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());
            //进度条的高度为三者最大的高度
            result = getPaddingTop()+getPaddingBottom()+Math.max(Math.max(mReachHeight,mUnReachHeight),Math.abs(textHeight));
            if(specMode == MeasureSpec.AT_MOST){
                result = Math.min(specSize,result);
            }
        }
        return result;
    }

    private int dp2px(int dpValue){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpValue,getResources().getDisplayMetrics());
    }
    private int sp2px(int spValue){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spValue,getResources().getDisplayMetrics());
    }
}

接下来我们要做的就是在代码中不断增加进度条的值

public class MainActivity extends Activity{

    private ProgressBarWithProgress progressBar;
    public static final int MSG_UPDATE = 0X110;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int currentProgress = progressBar.getProgress();
            progressBar.setProgress(++currentProgress);
            if(currentProgress>= progressBar.getMax()){
                mHandler.removeMessages(MSG_UPDATE);
            }else{
                mHandler.sendEmptyMessageDelayed(MSG_UPDATE,200);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = (ProgressBarWithProgress) findViewById(R.id.progress);
        mHandler.sendEmptyMessage(MSG_UPDATE);
    }

最后看看布局文件是如何写的

 <com.example.androidheros.ProgressBarWithProgress
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        custom:progress_reach_color="#ff00ff"
        custom:progress_reach_height="5dp"
        custom:progress_text_color="#000000"
        custom:progress_text_size="8sp"
        android:padding="10dp"
        custom:progress_unreach_color="#00ff00"
        custom:progress_unreach_height="3dp"
        custom:progress_text_margin="5dp"
        android:progress="0"
        style="@android:style/Widget.ProgressBar.Horizontal"
        >
    </com.example.androidheros.ProgressBarWithProgress>

圆形进度条

静图

这里写图片描述

动图

这里写图片描述

public class RoundProgressBar extends ProgressBar {
    //默认属性的值
    private static final int DEFAULT_REACH_COLOR = 0XFF00FF;
    private static final int DEFAULT_REACH_HEIGHT = 5;
    private static final int DEFAULT_UNREACH_COLOR = 0XFF00;
    private static final int DEFAULT_UNREACH_HEIGHT = 4;
    private static final int DEFAULT_TEXT_COLOR = 0X00FF;
    private static final int DEFAULT_TEXT_SIZE = 10;
    private static final int DEFAULT_TEXT_MARGIN = 8;
    private static final int DEFAULT_RADIUS = 30;
    private int mReachHeight = dp2px(DEFAULT_REACH_HEIGHT);
    private int mUnReachHeight = dp2px(DEFAULT_UNREACH_HEIGHT);
    private int mTextSize = sp2px(DEFAULT_TEXT_SIZE);
    private int mRadius = dp2px(DEFAULT_RADIUS);

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final int reachColor;
    private final float reachHeight;
    private final int unReachColor;
    private final float unReachHeight;
    private final int textColor;
    private final float textSize;
    private  float radius;
    private float maxPaintWidth;

    public RoundProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        //将自定义的属性封装到typedArray中
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);
        reachColor = typedArray.getColor(R.styleable.RoundProgressBar_progress_reach_color, DEFAULT_REACH_COLOR);
        reachHeight = typedArray.getDimension(R.styleable.RoundProgressBar_progress_reach_height, mReachHeight);
        unReachColor = typedArray.getColor(R.styleable.RoundProgressBar_progress_unreach_color, DEFAULT_UNREACH_COLOR);
        unReachHeight = typedArray.getDimension(R.styleable.RoundProgressBar_progress_unreach_height, mUnReachHeight);
        textColor = typedArray.getColor(R.styleable.RoundProgressBar_progress_text_color, DEFAULT_TEXT_COLOR);
        textSize = typedArray.getDimension(R.styleable.RoundProgressBar_progress_text_size,mTextSize);
        radius = typedArray.getDimension(R.styleable.RoundProgressBar_progress_radius, mRadius);
        typedArray.recycle();
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //取画笔的宽度为两个进度条宽度的最大值
        maxPaintWidth =  Math.max(reachHeight,unReachHeight);
        //默认四个padding一致,在没有设置进度条的宽高时使用这个值作为宽高的计算标准
        int espect = (int) (radius*2+maxPaintWidth+getPaddingRight()+getPaddingLeft());
        //更具测量模式获取对应的尺寸,实现细节可以参考源码
        int width = resolveSize(espect,widthMeasureSpec);
        int height = resolveSize(espect,heightMeasureSpec);
        //圆形进度条的实际宽度,取高和宽最小值,因为进度条是一个圆形
        int realWidth = Math.min(width,height);
        //圆形未到达进度条的半径,从涂上我们可以看出,因为画笔的一般在圆形的外面
        radius = (realWidth - getPaddingLeft() - getPaddingRight()-maxPaintWidth)/2;
        //保存测量值,这步必须做
        setMeasuredDimension(realWidth,realWidth);
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        mPaint.setTextSize(textSize);
        //中间显示的文字
        String text = getProgress()+"%";
        //文字宽度
        float textWidht = mPaint.measureText(text);
        //文字的高度,注意,这样写的前提是给Pain设置了textSize
        float textHeight = (mPaint.descent()-mPaint.ascent());
        canvas.save();
        //未到达进度。注意这个圆形的半径是包含画笔宽度的一半的,可以参照上面那张图
        mPaint.setStyle(Paint.Style.STROKE);
        //平移画布,目的是为了方便画圆
        canvas.translate(getPaddingLeft()+maxPaintWidth/2,getPaddingTop()+maxPaintWidth/2);
        mPaint.setColor(unReachColor);
        mPaint.setStrokeWidth(unReachHeight);
        canvas.drawCircle(radius,radius,radius,mPaint);
        //画已到达进度
        mPaint.setColor(reachColor);
        mPaint.setStrokeWidth(reachHeight);
        //根据当前进度计算弧度
        float sweepAngle = getProgress()*1.0f/getMax()*360;
        RectF rectF = new RectF(0,0,radius*2,radius*2);
        //从-90度开始画圆,
        canvas.drawArc(rectF,-90,sweepAngle+90/getMax()*360,false,mPaint);
        //画文字
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(textColor);
        canvas.drawText(text,radius-textWidht/2,radius+textHeight/4,mPaint);
        canvas.restore();
    }

    private int dp2px(int dpValue){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpValue,getResources().getDisplayMetrics());
    }
    private int sp2px(int spValue){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spValue,getResources().getDisplayMetrics());
    }
}

改变进度条的代码和水平进度条中的一样,这里就不贴出来了,请参照之前的代码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值