自定义进度条

自定义进度条

1.实现思路

  • 实现效果

  • 继承View

  • 重写protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);我们只需要考虑到控件的高度,宽度可以是填充父窗体

  • 在protected void onDraw(Canvas canvas)方法中绘制图形,大致思路:先绘制一个圆角矩形,然后使用直线绘制中间的进度条,最后绘制进度

2.onMeasure具体实现

具体代码实现如下

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final String progress = String.valueOf(getProgress());
        mTextPaint.getTextBounds(progress, 0, progress.length(), mBounds);
        final int bubbleHeight = (int) Math.ceil(getBubbleVerticalDisplacement());
        int measuredHeight;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            measuredHeight = resolveSizeAndState(bubbleHeight, heightMeasureSpec, 0);
        } else {
            measuredHeight = resolveSizeAndState2(bubbleHeight, heightMeasureSpec, 0);
        }
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), measuredHeight);
    }

在onMeasure方法中我们定义了一个resolveSizeAndState2()是为了解决3.0以下不支持这个方法,http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0316/1593.html

resolveSizeAndState2()方法如下:

    /**
     * 官方的resolveSizeAndState()需要3.0以上版本,所以自己写个resolveSizeAndState(),为了不与系统的发生冲突已resolveSizeAndState2()命名
     * @param size
     * @param measureSpec
     * @param childMeasuredState
     * @return
     */
    private int resolveSizeAndState2(int size, int measureSpec, int childMeasuredState) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
            if (specSize < size) {
                result = specSize | MEASURED_STATE_TOO_SMALL;
            } else {
                result = size;
            }
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result | (childMeasuredState & MEASURED_STATE_MASK);
    }

3.onDraw方法的具体实现

        @Override
        protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mListener != null) {
            if (mProgress == mMax) {
                mListener.onComplete();
            }
            mListener.onProgressChanged(mProgress, mMax);
        }

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        // 结束的位置
        int end = getWidth() - getPaddingRight();
        // 设置底部背景的颜色
        mLinesPaint.setColor(mSecondaryColor);
        // 画底部的背景
        RectF oval = new RectF(paddingLeft, getProgressTextHeight(), end - paddingRight, getProgressTextHeight() + mStrokeWidth);// 设置个新的长方形
        canvas.drawRoundRect(oval, mStrokeWidth, mStrokeWidth, mLinesPaint);//第二个参数是x半径,第三个参数是y半径
        // 画进度条
        mLinesPaint1.setColor(mPrimaryColor);
        Log.d("RoundLineProgressView", "当前进度=" + mProgress);
        int progressEnd = getProgressX(mProgress);
        // 计算出进度条的左右边距
        float ProgressPadding = (float) (mLinesPaint1.getStrokeWidth() * 0.6);
        float startX = ProgressPadding + paddingLeft;
        float startY = getProgressTextHeight() + mStrokeWidth / 2;
        float endX = progressEnd - ProgressPadding;
        float endY = getProgressTextHeight() + mStrokeWidth / 2;

        if (startX < endX) {
            canvas.drawLine(startX, startY, endX, endY, mLinesPaint1);
        } else {
            mLinesPaint1.setStyle(Paint.Style.FILL);
            canvas.drawCircle(startX, startY, lineWidth / 2, mLinesPaint1);
            mLinesPaint1.setStyle(Paint.Style.STROKE);
        }

        // 画百分比文本
        float textX = 0;
        if (progressEnd < mStrokeWidth) {
            textX = mStrokeWidth / 2 + paddingLeft;
        } else if (progressEnd > getWidth() - mStrokeWidth) {
            textX = getWidth() - mStrokeWidth / 2 - paddingRight;
        } else {
            textX = progressEnd;
        }
        float textY = mBounds.height() << 1;
        canvas.drawText(String.valueOf(mProgress), textX, textY, mTextPaint);
    }

4.完整代码

public class RoundLineProgressView extends View {

    public interface RoundLineProgressChangeListener {
        /**
         * 当前进度
         */
        void onProgressChanged(int progress, int max);

        /**
         * 进度完成
         */
        void onComplete();
    }

    private static final String TAG = "RoundLineProgressView";
    // Paint.ANTI_ALIAS_FLAG 使位图抗锯齿的标志
    private final Paint mLinesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint mLinesPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private RoundLineProgressChangeListener mListener;
    private final float m1Dip;
    private final float m1Sp;
    /**
     * 当前进度
     */
    private int mProgress;
    /**
     * 最大值
     */
    private int mMax;

    /**
     * 背景色
     */
    private int mPrimaryColor;
    /**
     * 当前进度的颜色
     */
    private int mSecondaryColor;

    /**
     * 宽度
     */
    private float mStrokeWidth;

    /**
     * 对进度文字的约束
     */
    private final Rect mBounds = new Rect();
    /**
     * 进度条线的宽度
     */
    private float lineWidth;

    public RoundLineProgressView(Context contex) {
        this(contex, null);
    }

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

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public RoundLineProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        m1Dip = getResources().getDisplayMetrics().density;
        m1Sp = getResources().getDisplayMetrics().scaledDensity;

        int max = 0;
        int progress = 0;
        float width = dips(8);
        int primaryColor = 0xFF009688;
        int secondaryColor = 0xFFDADADA;

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RopeProgressBar, defStyleAttr, 0);

        if (a != null) {
            max = a.getInt(R.styleable.RopeProgressBar_max, max);
            progress = a.getInt(R.styleable.RopeProgressBar_progress, progress);
            primaryColor = a.getColor(R.styleable.RopeProgressBar_primaryColor, primaryColor);
            secondaryColor = a.getColor(R.styleable.RopeProgressBar_secondaryColor, secondaryColor);
            width = a.getDimension(R.styleable.RopeProgressBar_progressWidth, width);
            a.recycle();
        }

        mPrimaryColor = primaryColor;
        mSecondaryColor = secondaryColor;
        mStrokeWidth = width;
        // 设置画笔的宽度
        mLinesPaint.setStrokeWidth(4);
        // 设置画笔的锯齿效果
        mLinesPaint.setAntiAlias(true);
        // 设置画笔为仅描边
        mLinesPaint.setStyle(Paint.Style.STROKE);
        lineWidth = (float) (width * 0.8);
        mLinesPaint1.setStrokeWidth(lineWidth);
        // 设置画笔为仅描边
        mLinesPaint1.setStyle(Paint.Style.STROKE);
        // 设置画笔笔刷类型
        mLinesPaint1.setStrokeCap(Paint.Cap.ROUND);

        mTextPaint.setColor(Color.BLACK);
        mTextPaint.setTextSize(sp(18));
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTypeface(Typeface.create("sans-serif-condensed-light", 0));

        setMax(max);
        setProgress(progress);
        // 硬件加速用的,需要在3.0以上使用
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(LAYER_TYPE_SOFTWARE, null);
        }

    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final String progress = String.valueOf(getProgress());
        mTextPaint.getTextBounds(progress, 0, progress.length(), mBounds);
        final int bubbleHeight = (int) Math.ceil(getBubbleVerticalDisplacement());
        int measuredHeight;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            measuredHeight = resolveSizeAndState(bubbleHeight, heightMeasureSpec, 0);
        } else {
            measuredHeight = resolveSizeAndState2(bubbleHeight, heightMeasureSpec, 0);
        }
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), measuredHeight);
    }

    /**
     * 官方的resolveSizeAndState()需要3.0以上版本,所以自己写个resolveSizeAndState(),为了不与系统的发生冲突已resolveSizeAndState2()命名
     * @param size
     * @param measureSpec
     * @param childMeasuredState
     * @return
     */
    private int resolveSizeAndState2(int size, int measureSpec, int childMeasuredState) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
            if (specSize < size) {
                result = specSize | MEASURED_STATE_TOO_SMALL;
            } else {
                result = size;
            }
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result | (childMeasuredState & MEASURED_STATE_MASK);
    }

    private double getBubbleVerticalDisplacement() {
        return getProgressTextMargin() + getProgressTextHeight() + +mStrokeWidth;
    }

    /**
     * 计算出百分比进度文本的高度
     * @return
     */
    private float getProgressTextHeight() {
        return mBounds.height() + dips(16);
    }

    /**
     * 设置一个默认的Margin
     * @return
     */
    private float getProgressTextMargin() {
        return dips(4);
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mListener != null) {
            if (mProgress == mMax) {
                mListener.onComplete();
            }
            mListener.onProgressChanged(mProgress, mMax);
        }

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        // 结束的位置
        int end = getWidth() - getPaddingRight();
        // 设置底部背景的颜色
        mLinesPaint.setColor(mSecondaryColor);
        // 画底部的背景
        RectF oval = new RectF(paddingLeft, getProgressTextHeight(), end - paddingRight, getProgressTextHeight() + mStrokeWidth);// 设置个新的长方形
        canvas.drawRoundRect(oval, mStrokeWidth, mStrokeWidth, mLinesPaint);//第二个参数是x半径,第三个参数是y半径
        // 画进度条
        mLinesPaint1.setColor(mPrimaryColor);
        Log.d("RoundLineProgressView", "当前进度=" + mProgress);
        int progressEnd = getProgressX(mProgress);
        // 计算出进度条的左右边距
        float ProgressPadding = (float) (mLinesPaint1.getStrokeWidth() * 0.6);
        float startX = ProgressPadding + paddingLeft;
        float startY = getProgressTextHeight() + mStrokeWidth / 2;
        float endX = progressEnd - ProgressPadding;
        float endY = getProgressTextHeight() + mStrokeWidth / 2;

        if (startX < endX) {
            canvas.drawLine(startX, startY, endX, endY, mLinesPaint1);
        } else {
            mLinesPaint1.setStyle(Paint.Style.FILL);
            canvas.drawCircle(startX, startY, lineWidth / 2, mLinesPaint1);
            mLinesPaint1.setStyle(Paint.Style.STROKE);
        }

        // 画百分比文本
        float textX = 0;
        if (progressEnd < mStrokeWidth) {
            textX = mStrokeWidth / 2 + paddingLeft;
        } else if (progressEnd > getWidth() - mStrokeWidth) {
            textX = getWidth() - mStrokeWidth / 2 - paddingRight;
        } else {
            textX = progressEnd;
        }
        float textY = mBounds.height() << 1;
        canvas.drawText(String.valueOf(mProgress), textX, textY, mTextPaint);
    }

    /**
     * 计算当前进度的X坐标
     * @param progress
     * @return
     */
    private int getProgressX(int progress) {
        int progressX = 0;
        progressX = (getWidth() - getPaddingLeft() - getPaddingRight()) * progress / mMax;
        Log.d(TAG, "当前进度对应的屏幕坐标progressX=" + progressX);
        return progressX;
    }

    public void setMax(int max) {
        max = Math.max(0, max);
        if (max != mMax) {
            mMax = max;
            if (mProgress > max) {
                mProgress = max;
            }
            postInvalidate();
        }
    }

    public int getMax() {
        return mMax;
    }

    /**
     * 获取当前进度
     * @return
     */
    public int getProgress() {
        return mProgress;
    }

    public void setOnChangeListener(RoundLineProgressChangeListener mListener) {
        this.mListener = mListener;
    }

    /**
     * 设置当前进度
     * @param progress
     */
    public synchronized void setProgress(int progress) {
        Log.d(TAG, "外部内传递过来的进度=" + progress);
        if (this.mProgress == progress) {
            return;
        }
        this.mProgress = progress;
        postInvalidate();
    }

    private float dips(final float dips) {
        return dips * m1Dip;
    }

    private float sp(final int sp) {
        return sp * m1Sp;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值