美文网首页自定义控件
Android-自定义View-自定义进度条

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

作者: 844b9a3a3a68 | 来源:发表于2018-06-04 16:00 被阅读258次

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

    需求分析:


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

    效果图:

    效果图

    开始撸码:

    新建类继承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);
        }
    

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

        <view.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-自定义View-自定义进度条

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