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

简介

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

分析

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

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

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

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

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

代码

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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);
    }

绘制图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 绘制文本背景框
 * @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);
 
}

绘制文本

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 绘制文本
 * @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);
}

绘制默认进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 绘制背景矩形圆角
 * @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对,表示矩形的一个角,分别为左上,右上,右下,左下(顺时针)。

绘制进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
 * 绘制进度矩形圆角(只有左上和左下是圆角)
 * @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,从而实现左边圆弧,右边直线的圆角矩形效果。

开放的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * 设置是否矩形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时,文本和文本背景图如何显示;进度显示是静态的,如何动态实现显示呢等等。我们可以根据具体的需求去控制,我就不一一描述了。

posted @   ha_cjy  阅读(1446)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示