最近写了两个自定义进度条,一个静态的,一个带动画的,都比较简单,直接继承View,但是带动画的进度条应该还有更好的实现方法,如果有大神路过就指点一下吧!
静态的ProgressView,要求进度条的进度头部,带一个百分数,说明现在到达百分之几,效果图如下:
进度条上方文字不超出进度条两侧,其次,进度条上方文字的中间与进度条头部对齐。
我来分解一下,这里有三部分,第一部分是文字,我们使用一个TextPaint来画,第二第三部分是paogress的前景与后景。
然后是作图,首先我们要获取各种数据。
横向:屏幕宽度、文字宽度
纵向:文字高度、进度条高度以及两者间隙
确定需要的变量或常量:
private float progress;
private TextPaint textPaint;
private float textHeight;
private float textProgressPadding;
private Paint backgroundPaint, foregroundPaint;
private float progressHeight;
private DecimalFormat df;
numberWidth是三个固定的宽度,在初始化的时候就算好。
设置一个初始化方法:
private void initView() {
textHeight = DisplayUtils.sp2px(getContext(), 13f);
textProgressPadding = DisplayUtils.dip2px(getContext(), 3f);
progressHeight = DisplayUtils.dip2px(getContext(), 2.5f);
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(0xffff9600);
textPaint.setTextSize(textHeight);
foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
foregroundPaint.setColor(0xffff9600);
foregroundPaint.setStyle(Paint.Style.FILL);
backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backgroundPaint.setColor(0xffeeeeee);
backgroundPaint.setStyle(Paint.Style.FILL);
df = new DecimalFormat("0.00");
progress = 0;
}
除2是为了获取半宽,因为要把文字居中(文字绘制是从x=0,y=baseline处开始的,这三个数字针对的是x方向)
View的绘制过程是onMeasure,onLayout,onDrwa,而onMeasure完成后我们就可以直接通过getWidth()获取屏幕宽度了。
好,我们先完成onMeasure,这里宽度随屏幕,高度写死:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int width, height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = getSuggestedMinimumWidth();
}
height = (int) Math.ceil(textHeight + textProgressPadding + progressHeight);
setMeasuredDimension(width, height);
}
然后完成onDraw:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float currentProgressWidth = getWidth() * progress / 100;
String progressStr = df.format(progress) + "%";
float halfTextWidth = textPaint.measureText(progressStr) / 2;
if (currentProgressWidth < halfTextWidth) {//左对齐
canvas.drawText(progressStr, 0, -fontMetrics.ascent, textPaint);
} else if (getWidth() - currentProgressWidth < halfTextWidth) {//右对齐
canvas.drawText(progressStr, getWidth() - halfTextWidth * 2, -fontMetrics.ascent, textPaint);
} else {//居中
canvas.drawText(progressStr, currentProgressWidth - halfTextWidth, -fontMetrics.ascent, textPaint);
}
canvas.drawRect(
0,
textHeight + textProgressPadding,
getWidth(),
textHeight + textProgressPadding + progressHeight,
backgroundPaint
);
canvas.drawRect(
0,
textHeight + textProgressPadding,
currentProgressWidth,
textHeight + textProgressPadding + progressHeight,
foregroundPaint
);
}
FrontMetrics可以获取文字的各种高度宽度,ascent就是baseline以上的高度,不过它取出来的时候是个负数。
上面if-else的逻辑是:左边尽头,进度条宽度小于文字半宽,文字就靠左贴边,右边尽头,进度条所剩宽度小于文字半宽,文字就靠右贴边,不满足以上判断条件的就居中(计算的时候分两种情况:0.0%,00.0%)。
最后,加上progress的set方法,达到100的时候让它变红,不是100的时候都是橙色:
public void setProgress(float progress) {
if (progress < 0) progress = 0;
if (progress >= 100) {
//progress = 100;
textPaint.setColor(0xfff74b4b);
foregroundPaint.setColor(0xfff74b4b);
} else {
textPaint.setColor(0xffff9600);
foregroundPaint.setColor(0xffff9600);
}
this.progress = (float)(Math.round(progress*100))/100;//保留两位小数
invalidate();
}
还有构造方法:
public SuperDaTaoProgress(Context context) {
super(context);
initView();
}
public SuperDaTaoProgress(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}