• Android自定义View:进度条+冒泡文本


    简介

    最近看到有这样的需求:显示进度条,描述文本显示在进度条的刻度上面。正好练练手,回顾下自定义View知识。

    分析

    通过上图,我们可以看到,该UI显示了文本,而文本显示在一张图片中,有一个默认的进度条和根据实际进度显示的进度条。我们可以将其拆分成4个组成部分:

    (1)图片,作为文本的背景图,这个背景图应该伸缩不失真,建议用.9图;

    (2)文本,有颜色和大小,显示在图片上,居中显示;

    (3)默认进度条,有颜色和宽高;

    (4)真正的进度条:有颜色和进度值、宽高。它比较特殊的是当进度不等于100%时,左边是圆弧,右边是直线型(没有圆角)。

    代码

    首先在构造函数初始化画笔,在onSizeChanged中确定控件的宽高,设置画笔的颜色,然后在onDraw中开始绘制内容

    private Context mContext;
        /**
         * 背景图画笔
         */
        private Paint mBackgroundPaint;
        /**
         * 进度画笔
         */
        private Paint mProgressPaint;
        /**
         * 文本画笔
         */
        private Paint mTextPaint;
        /**
         * 图片画笔
         */
        private Paint mPicturePaint;
        /**
         * 背景矩形颜色
         */
        private int mBackgroundColor = Color.BLACK;
        /**
         * 进度矩形颜色
         */
        private int mProgressColor = Color.GRAY;
        /**
         * 文本颜色
         */
        private int mTextColor = Color.BLACK;
        /**
         * 文本大小
         */
        private int mTextSize = 28;
        /**
         * 控件宽高:控件必须在布局中指定宽高大小
         */
        private int mWidth;
        private int mHeight;
        /**
         * 进度矩形高度
         */
        private int mProgressHeight = 10;
        /**
         * 图片宽高
         */
        private int mPicWidth;
        private int mPicHeight;
        /**
         * 矩形4个角的半径坐标,左上,右上,右下,左下(顺时针)
         */
        private float[] mRadiusArr = new float[]{0f,0f,0f,0f,0f,0f,0f,0f};
        /**
         * 进度
         */
        private float mProgress;
        /**
         * 文本
         */
        private String mText;
    
        public ProgressBubbleView(Context context) {
            super(context);
            initView(context);
        }
    
        public ProgressBubbleView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }
    
        public ProgressBubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    
        private void initView(Context context){
            mContext = context;
    
            mBackgroundPaint = new Paint();
            mBackgroundPaint.setStyle(Paint.Style.FILL);
            mBackgroundPaint.setStrokeWidth(1);
    
            mBackgroundPaint.setAntiAlias(true);
    
            mProgressPaint = new Paint();
            mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mProgressPaint.setStrokeWidth(2);
            mProgressPaint.setAntiAlias(true);
    
            mTextPaint = new Paint();
            mTextPaint.setStyle(Paint.Style.STROKE);
            mTextPaint.setTextSize(mTextSize);
            mTextPaint.setAntiAlias(true);
    
            mPicturePaint = new Paint();
            mPicturePaint.setStyle(Paint.Style.STROKE);
            mPicturePaint.setAntiAlias(true);
            setLayerType(LAYER_TYPE_SOFTWARE,null);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidth = w;
            mHeight = h;
    
            mBackgroundPaint.setColor(mBackgroundColor);
            mProgressPaint.setColor(mProgressColor);
            mTextPaint.setColor(mTextColor);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            //绘制文本背景框
            drawPicture(canvas);
            //绘制文本
            drawText(canvas);
            //将原点坐标移到文本之下
            canvas.translate(0,mPicHeight-28);//因为图片底部有留白,导致图片与进度条的距离看起来偏大,所以减去一段距离
            //绘制背景圆角矩形
            drawBackgroud(canvas);
            //绘制进度矩形圆角
            drawProgress(canvas);
        }

    绘制图片

        /**
         * 绘制文本背景框
         * @param canvas
         */
        private void drawPicture(Canvas canvas){
            if(this.mProgress == 0)
                return;
            float percent = this.mProgress / 100 * 1.0f;
            int width = (int) (percent * mWidth);
            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.infowindow_bg);
            mPicWidth = bitmap.getHeight();
            mPicHeight = bitmap.getWidth();
            int pos = width - mPicWidth/2 - 13;
            canvas.drawBitmap(bitmap,pos,0,mPicturePaint);
    
        }
    

    绘制文本

        /**
         * 绘制文本
         * @param canvas
         */
        private void drawText(Canvas canvas){
            if(this.mProgress == 0)
                return;
            float percent = this.mProgress / 100 * 1.0f;
            int width = (int) (percent * mWidth);
            float length = mTextPaint.measureText(mText);
            canvas.drawText(mText,width-length/2,mPicHeight/3,mTextPaint);
        }
    

    绘制默认进度条

        /**
         * 绘制背景矩形圆角
         * @param canvas
         */
        private void drawBackgroud(Canvas canvas){
            RectF rectF = new RectF();
            rectF.left = 0;
            rectF.top = 0;
            rectF.right = mWidth;
            rectF.bottom = mProgressHeight;
    
            allRoundRadius();
            Path path = new Path();
            path.addRoundRect(rectF, mRadiusArr,Path.Direction.CW);
            canvas.drawPath(path,mBackgroundPaint);
        }

    这里利用path类的addRoundRect方法的第2个参数float[] radii来实现矩形四个角是圆角还是直角效果。radii是一个大小为8的数组,2个元素为1对,表示矩形的一个角,分别为左上,右上,右下,左下(顺时针)。

    绘制进度条

        /**
         * 绘制进度矩形圆角(只有左上和左下是圆角)
         * @param canvas
         */
        private void drawProgress(Canvas canvas){
            if(this.mProgress == 0)
                return;
            float percent = this.mProgress / 100 * 1.0f;
            int width = (int) (percent * mWidth);
    
            RectF rectF = new RectF();
            rectF.left = 0;
            rectF.top = 0;
            rectF.right = width;
            rectF.bottom = mProgressHeight;
    
            if (this.mProgress < 100){
                mRadiusArr[0] = 20;
                mRadiusArr[1] = 20;
                mRadiusArr[2] = 0;
                mRadiusArr[3] = 0;
                mRadiusArr[4] = 0;
                mRadiusArr[5] = 0;
                mRadiusArr[6] = 20;
                mRadiusArr[7] = 20;
            }else{
                allRoundRadius();
            }
            Path path = new Path();
            path.addRoundRect(rectF,mRadiusArr,Path.Direction.CW);
            canvas.drawPath(path,mProgressPaint);
        }
    
        /**
         * 4个角都是圆角
         */
        private void allRoundRadius(){
            mRadiusArr[0] = 20;
            mRadiusArr[1] = 20;
            mRadiusArr[2] = 20;
            mRadiusArr[3] = 20;
            mRadiusArr[4] = 20;
            mRadiusArr[5] = 20;
            mRadiusArr[6] = 20;
            mRadiusArr[7] = 20;
        }
    

    在drawProgress中,当进度不等于100%时,左边是圆弧,右边是直线型,所以设置左上、左下的圆角半径为20,其他2个角半径为0,从而实现左边圆弧,右边直线的圆角矩形效果。

    开放的接口

        /**
         * 设置是否矩形4个角都是圆角
         * @param isAllRound
         */
        public void setFourRoundRect(boolean isAllRound){
            if (isAllRound){
                allRoundRadius();
            }
            invalidate();
        }
    
        /**
         * 设置进度值0-100
         * @param progress
         */
        public void setProgress(float progress){
            this.mProgress = progress;
            invalidate();
        }
    
        /**
         * 设置文本
         * @param text
         */
        public void setText(String text){
            this.mText = text;
            invalidate();
        }
    
        public void  setBackgroundColor(int backgroundColor){
            this.mBackgroundColor = backgroundColor;
            invalidate();
        }
    
        public void  setProgressColor(int progressColor){
            this.mProgressColor = progressColor;
            invalidate();
        }
    
        public void  setTextColor(int textColor){
            this.mTextColor = textColor;
            invalidate();
        }
    

    效果图

    最后

    当然,这只是粗略的实现,还是有些问题遗留的。比如进度为0或100时,文本和文本背景图如何显示;进度显示是静态的,如何动态实现显示呢等等。我们可以根据具体的需求去控制,我就不一一描述了。

  • 相关阅读:
    常用的SQL优化
    mysql索引详细介绍
    作业2
    作业1
    python学习笔记(11)文件操作
    python学习笔记(10)函数(二)
    python学习笔记(9)函数(一)
    C#的dictionary使用总结
    常用的类型转化
    我的动态库“情节”
  • 原文地址:https://www.cnblogs.com/hacjy/p/7390479.html
Copyright © 2020-2023  润新知