Android自定义View-NumberProgress
2016-03-23 本文已影响641人
晓峰残月
前言
个人网站已经上线运行,欢迎各位收藏:http://jwenfeng.com
在 android 应用的开发中 ProgressBar 是一个必不可少的控件之一,一般有两种形态,一种事圆形的 ProgressBar,它主要在加载图片,加载更多,刷新等场景中使用,另一种就是横向的 ProgressBar,它主要用来显示下载的进度等等。
下面我自定义的这个 NumberProgress 和系统的 ProgressBar 没有什么关系,是继承View自定义的一个控件,看下图:
分析
NumberProgress的构成如下图,主要有3块,左边 Reached 部分,中间的文字部分,以及右边的 UnReached 部分。
实现
1. 首先定义属性
在value文件夹中的attrs.xml中添加以下属性
<declare-styleable name="NumberProgress">
<!-- 文字大小 -->
<attr name="textSize" format="dimension"/>
<!-- 文字颜色 -->
<attr name="textColor" format="color"/>
<!-- reached的高度 -->
<attr name="reachedHeight" format="dimension"/>
<!-- reached的颜色 -->
<attr name="reachedColor" format="color"/>
<!-- unReached的高度 -->
<attr name="unReachedHeight" format="dimension"/>
<!-- unReached的颜色 -->
<attr name="unReachedColor" format="color"/>
<!-- 当前进度 -->
<attr name="currentProgress" format="integer"/>
<!-- 总进度 -->
<attr name="maxProgress" format="integer"/>
</declare-styleable>
2. 在 NumberProgress 中获取定义属性
/**
* 百分比文字颜色
* */
private int textColor;
/**
* 百分比文字大小
* */
private float textSize;
/**
* reached部分的颜色
* */
private int reachedColor;
/**
* reached部分的高度
* */
private float reachedHeight;
/**
* unReached部分的颜色
* */
private int unReachedColor;
/**
* unReached部分的高度
* */
private float unReachedHeight;
/**
* 当前进度
* */
private int currentProgress;
/**
* 总进度
* */
private int maxProgress;
public NumberProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.NumberProgress);
textColor = a.getColor(R.styleable.NumberProgress_textColor, Color.parseColor("#3498DB"));
textSize = sp2px(context, a.getDimension(R.styleable.NumberProgress_textSize, 14f));
reachedHeight = dip2px(context, a.getDimension(R.styleable.NumberProgress_reachedHeight, 1.25f));
reachedColor = a.getColor(R.styleable.NumberProgress_reachedColor, Color.parseColor("#3498DB"));
unReachedHeight = dip2px(context, a.getDimension(R.styleable.NumberProgress_unReachedHeight, 0.75f));
unReachedColor = a.getColor(R.styleable.NumberProgress_unReachedColor, Color.parseColor("#CCCCCC"));
currentProgress = a.getInt(R.styleable.NumberProgress_currentProgress, 0);
maxProgress = a.getInt(R.styleable.NumberProgress_maxProgress, 100);
reachedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
reachedPaint.setColor(reachedColor);
unReachedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
unReachedPaint.setColor(unReachedColor);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(textColor);
textPaint.setTextSize(textSize);
a.recycle();
}
3. 计算控件的大小
一个MeasureSpec由大小和模式组成。
它有三种模式:
- UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
- EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
- AT_MOST(至多),子元素至多达到指定大小的值。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 一个MeasureSpec由大小和模式组成。
* 它有三种模式:
* UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
* EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
* AT_MOST(至多),子元素至多达到指定大小的值。
* */
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
private int measureHeight(int heightMeasureSpec) {
int result;
int mode = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);
int padding = getPaddingTop()+getPaddingBottom();
if (mode == MeasureSpec.EXACTLY){
result = size;
}else{
result = getSuggestedMinimumHeight();
result += padding;
if (mode == MeasureSpec.AT_MOST){
result = Math.min(result, size);
}
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int result;
int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);
int padding = getPaddingLeft()+getPaddingRight();
if (mode == MeasureSpec.EXACTLY){
result = size;
}else{
result = getSuggestedMinimumWidth();
result += padding;
if (mode == MeasureSpec.AT_MOST){
result = Math.max(result, size);
}
}
return result;
}
@Override
protected int getSuggestedMinimumWidth() {
return (int) textSize;
}
@Override
protected int getSuggestedMinimumHeight() {
return Math.max((int) textSize, Math.max((int) reachedHeight, (int) unReachedHeight));
}
4. 计算 reached、unReached 和文字的大小以及位置
在计算中有3中情况:
- 进度为0的时候。当进度为0的时候reached是不用绘制的,需要绘制文字和unReached部分
- 进度不为0的时候。当进度不为0的时候,3个部分都需要绘制。
- 当reached部分和文字部分大于等于控件的宽度的时候。这个时候 unReached 部分不需要绘制,且reached的右边的位置为整个宽度减去文字宽度。
@Override
protected void onDraw(Canvas canvas) {
calculateDrawRectF();
if (drawReachedBar) {
canvas.drawRect(reachedRectF, reachedPaint);
}
canvas.drawText(currentDrawText, drawTextStart, drawTextEnd, textPaint);
if (dawUnreachedBar) {
canvas.drawRect(unReachedRectF, unReachedPaint);
}
}
private void calculateDrawRectF() {
currentDrawText = String.format("%d", currentProgress * 100 / maxProgress);
currentDrawText = currentDrawText+"%";
float drawTextWidth = textPaint.measureText(currentDrawText);
if (currentProgress == 0){
drawReachedBar = false;
drawTextStart = getPaddingLeft();
reachedRectF.right = 0;
}else{
drawReachedBar = true;
reachedRectF.left = getPaddingLeft();
reachedRectF.top = getHeight()/2.0f-reachedHeight/2.0f;
reachedRectF.right = (getWidth()-getPaddingLeft()-getPaddingRight())/(maxProgress*1.0f) * (float)currentProgress;
reachedRectF.bottom = getHeight()/2.0f+reachedHeight/2.0f;
drawTextStart = reachedRectF.right;
}
if ((drawTextStart+drawTextWidth) >= (getWidth()-getPaddingRight())){
drawTextStart = getWidth() - getPaddingRight() - drawTextWidth;
reachedRectF.right = drawTextStart;
}
drawTextEnd = (int) ((getHeight() / 2.0f) - ((textPaint.descent() + textPaint.ascent()) / 2.0f));
float unreachedBarStart = reachedRectF.right+drawTextWidth;
if (unreachedBarStart >= getWidth() - getPaddingRight()) {
dawUnreachedBar = false;
}else{
dawUnreachedBar = true;
unReachedRectF.left = unreachedBarStart;
unReachedRectF.right = getWidth() - getPaddingRight();
unReachedRectF.top = getHeight() / 2.0f + -unReachedHeight / 2.0f;
unReachedRectF.bottom = getHeight() / 2.0f + unReachedHeight / 2.0f;
}
}
5. 测试
测试就没什么说的了
numberProgress = (NumberProgress) findViewById(R.id.number_progress);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
current++;
if (current > 100){
current = 0;
}
numberProgress.setCurrentProgress(current);
handler.postDelayed(this,80);
}
},80);
效果图:
效果图
完整代码请移步github:https://github.com/823546371/NumberProgress
本文地址:http://www.jianshu.com/p/755a6bd6a6cc
尊重原创,转载请注明:From 晓峰残月(http://jwenfeng.com) 侵权必究!