Android-自定义View-自定义进度条

眼看6月到了,由于前段时间域名备案等原因,服务器关闭了差不多一个月,所以没更新文章,索性今天补一篇吧,准备写一个简单的自定义View,就拿进度条做这个需求吧,虽然简单,但是也包含了基本自定义View的几要素,比如自定义属性、重写测量、重写绘制等功能。

需求分析:


  • 1.进度通过绘制线条实现。
  • 2.进度文字跟随当前进度实时变化,并非一直显示在固定位置。
  • 3.控件未给出宽高属性时,我们需要给出默认值,具体以文字大小而定。
  • 4.为了满足开发需求,自定义属性要多,能够最大程度的让开发者控制View的某个属性。

效果图:

Android-自定义View-自定义进度条_第1张图片

开始撸码:

新建类继承View,并实现构造方法:

public class ProgressBarView extends View {

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

    public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

}

新建attrs.xml,并编写自定义属性:

    <declare-styleable name="ProgressBarView">
        <attr name="progress_left_color" format="color" />
        <attr name="progress_center_color" format="color" />
        <attr name="progress_right_color" format="color" />
        <attr name="progress_left_height" format="dimension" />
        <attr name="progress_right_height" format="dimension" />
        <attr name="progress_text_size" format="dimension" />
        <attr name="progress_current_progress" format="integer" />
    declare-styleable>

声明变量,并接收自定义属性的值(记得初始化方法在构造中调用):

    /**
     * 画笔
     */
    private Paint mPaint;
    /**
     * 默认进度值
     */
    private int mProgress = 0;
    /**
     * 进度边距
     */
    private int mProgressPadding = dp2px(2);
    /**
     * 控件高度
     */
    private int mHeight = 0;

    /**
     * 字体大小
     */
    float mTextSize = 18f;

    /**
     * 左边进度条颜色
     */
    int mLeftColor = Color.RED;
    /**
     * 中间文字条颜色
     */
    int mCenterColor = Color.RED;
    /**
     * 右边进度条颜色
     */
    int mRightColor = Color.RED;
    /**
     * 左边进度条高度
     */
    int mLeftHeight = dp2px(10);
    /**
     * 右边进度条高度
     */
    int mRightHeight = dp2px(10);



     /**
     * 初始化
     */
    private void init(AttributeSet attrs) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setColor(Color.RED);//画笔颜色
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
        mLeftColor = ta.getColor(R.styleable.ProgressBarView_progress_left_color, mLeftColor);
        mCenterColor = ta.getColor(R.styleable.ProgressBarView_progress_center_color, mCenterColor);
        mRightColor = ta.getColor(R.styleable.ProgressBarView_progress_right_color, mRightColor);
        mLeftHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_left_height, mLeftHeight);
        mRightHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_right_height, mRightHeight);
        mTextSize = ta.getDimension(R.styleable.ProgressBarView_progress_text_size, mTextSize);
        mProgress = ta.getInt(R.styleable.ProgressBarView_progress_current_progress, mProgress);
        ta.recycle();
        mPaint.setTextSize(mTextSize);//设定文字大小,后续好测量文字高度
    }

重写 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法测量自身:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);//默认用户需要给出明确值,所以不判断模式
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(widthVal, height);//设置了测量值后,可以获取测量值。
    }

    /**
     * 测量高度
     *
     * @param heightMeasureSpec
     * @return
     */
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);//得到测量模式
        int size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {//用户给了精确值
            result = size;
        } else { //MeasureSpec.UNSPECIFIED  MeasureSpec.AT_MOST 未指定明确参数
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());//得到文字高度
            result = getPaddingTop() + getPaddingBottom() + Math.max(mHeight, textHeight);//高度等于进度条高度和文字高度中最高的为准,并且加上padding值
            if (mode == MeasureSpec.AT_MOST) {//给定了最大值
                result = Math.min(result, size);
            }
        }
        return result;
    }

重写 protected void onDraw(Canvas canvas)并实现自定义视图绘制:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mHeight = getMeasuredHeight();
        String str = mProgress + "%";
        float textWidth = mPaint.measureText(str);//文本宽度
        float width = getMeasuredWidth() - textWidth - getPaddingLeft() - getPaddingRight() - mProgressPadding * 2;//控件宽度-文本宽度-padding=进度条总宽度
        float currentProgress = getPaddingLeft() + width * mProgress / 100;//当前进度应该绘制的位置
        mPaint.setColor(mLeftColor);
        mPaint.setStrokeWidth(mLeftHeight);//画笔宽度
        canvas.drawLine(getPaddingLeft(), mHeight / 2, currentProgress, mHeight / 2, mPaint);
        int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
        mPaint.setColor(mCenterColor);
        canvas.drawText(str, currentProgress + mProgressPadding, mHeight / 2 + y, mPaint);
        mPaint.setColor(mRightColor);
        mPaint.setStrokeWidth(mRightHeight);
        canvas.drawLine(currentProgress + textWidth + mProgressPadding * 2, mHeight / 2, getMeasuredWidth() - getPaddingRight(), mHeight / 2, mPaint);
    }

在布局中引用编写的控件,并设置自定义属性:

    .peakchao.view.view.ProgressBarView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        app:progress_center_color="#ff00ff"
        app:progress_current_progress="80"
        app:progress_left_color="#f00"
        app:progress_left_height="1dp"
        app:progress_right_color="#0f0"
        app:progress_right_height="2dp"
        app:progress_text_size="30dp" />

至此控件已经能够正常显示了,但是由于目前只是写个Demo,还有没考虑的东西,比如各个属性的get和set方法等,需要自己根据需求完善。

附上ProgressBarView完整代码:

/**
 * Created by Chao  2018/6/1 on 17:49
 * description
 */

public class ProgressBarView extends View {
    /**
     * 画笔
     */
    private Paint mPaint;
    /**
     * 默认进度值
     */
    private int mProgress = 0;
    /**
     * 进度边距
     */
    private int mProgressPadding = dp2px(2);
    /**
     * 控件高度
     */
    private int mHeight = 0;

    /**
     * 字体大小
     */
    float mTextSize = 18f;

    /**
     * 左边进度条颜色
     */
    int mLeftColor = Color.RED;
    /**
     * 中间文字条颜色
     */
    int mCenterColor = Color.RED;
    /**
     * 右边进度条颜色
     */
    int mRightColor = Color.RED;
    /**
     * 左边进度条高度
     */
    int mLeftHeight = dp2px(10);
    /**
     * 右边进度条高度
     */
    int mRightHeight = dp2px(10);


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

    public ProgressBarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    /**
     * 初始化
     */
    private void init(AttributeSet attrs) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setColor(Color.RED);//画笔颜色
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
        mLeftColor = ta.getColor(R.styleable.ProgressBarView_progress_left_color, mLeftColor);
        mCenterColor = ta.getColor(R.styleable.ProgressBarView_progress_center_color, mCenterColor);
        mRightColor = ta.getColor(R.styleable.ProgressBarView_progress_right_color, mRightColor);
        mLeftHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_left_height, mLeftHeight);
        mRightHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_right_height, mRightHeight);
        mTextSize = ta.getDimension(R.styleable.ProgressBarView_progress_text_size, mTextSize);
        mProgress = ta.getInt(R.styleable.ProgressBarView_progress_current_progress, mProgress);
        ta.recycle();
        mPaint.setTextSize(mTextSize);//设定文字大小,后续好测量文字高度
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);//得到测量模式
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);//默认用户需要给出明确值,所以不判断模式
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(widthVal, height);//设置了测量值后,可以获取测量值。
        //mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }

    /**
     * 测量高度
     *
     * @param heightMeasureSpec
     * @return
     */
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);//得到测量模式
        int size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {//用户给了精确值
            result = size;
        } else { //MeasureSpec.UNSPECIFIED  MeasureSpec.AT_MOST 未指定明确参数
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());//得到文字高度
            result = getPaddingTop() + getPaddingBottom() + Math.max(mHeight, textHeight);//高度等于进度条高度和文字高度中最高的为准,并且加上padding值
            if (mode == MeasureSpec.AT_MOST) {//给定了最大值
                result = Math.min(result, size);
            }
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mHeight = getMeasuredHeight();
        String str = mProgress + "%";
        float textWidth = mPaint.measureText(str);//文本宽度
        float width = getMeasuredWidth() - textWidth - getPaddingLeft() - getPaddingRight() - mProgressPadding * 2;//控件宽度-文本宽度-padding=进度条总宽度
        float currentProgress = getPaddingLeft() + width * mProgress / 100;//当前进度应该绘制的位置
        mPaint.setColor(mLeftColor);
        mPaint.setStrokeWidth(mLeftHeight);//画笔宽度
        canvas.drawLine(getPaddingLeft(), mHeight / 2, currentProgress, mHeight / 2, mPaint);
        int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
        mPaint.setColor(mCenterColor);
        canvas.drawText(str, currentProgress + mProgressPadding, mHeight / 2 + y, mPaint);
        mPaint.setColor(mRightColor);
        mPaint.setStrokeWidth(mRightHeight);
        canvas.drawLine(currentProgress + textWidth + mProgressPadding * 2, mHeight / 2, getMeasuredWidth() - getPaddingRight(), mHeight / 2, mPaint);
    }

    private int dp2px(int val) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, val, getResources().getDisplayMetrics());
    }

    private int sp2px(int val) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, val, getResources().getDisplayMetrics());
    }
}

你可能感兴趣的:(Android)