自定义进度条
1.实现思路
实现效果
继承View
重写protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);我们只需要考虑到控件的高度,宽度可以是填充父窗体
在protected void onDraw(Canvas canvas)方法中绘制图形,大致思路:先绘制一个圆角矩形,然后使用直线绘制中间的进度条,最后绘制进度
2.onMeasure具体实现
具体代码实现如下
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final String progress = String.valueOf(getProgress());
mTextPaint.getTextBounds(progress, 0, progress.length(), mBounds);
final int bubbleHeight = (int) Math.ceil(getBubbleVerticalDisplacement());
int measuredHeight;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
measuredHeight = resolveSizeAndState(bubbleHeight, heightMeasureSpec, 0);
} else {
measuredHeight = resolveSizeAndState2(bubbleHeight, heightMeasureSpec, 0);
}
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), measuredHeight);
}
在onMeasure方法中我们定义了一个resolveSizeAndState2()是为了解决3.0以下不支持这个方法,http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0316/1593.html
resolveSizeAndState2()方法如下:
/**
* 官方的resolveSizeAndState()需要3.0以上版本,所以自己写个resolveSizeAndState(),为了不与系统的发生冲突已resolveSizeAndState2()命名
* @param size
* @param measureSpec
* @param childMeasuredState
* @return
*/
private int resolveSizeAndState2(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
3.onDraw方法的具体实现
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mListener != null) {
if (mProgress == mMax) {
mListener.onComplete();
}
mListener.onProgressChanged(mProgress, mMax);
}
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
// 结束的位置
int end = getWidth() - getPaddingRight();
// 设置底部背景的颜色
mLinesPaint.setColor(mSecondaryColor);
// 画底部的背景
RectF oval = new RectF(paddingLeft, getProgressTextHeight(), end - paddingRight, getProgressTextHeight() + mStrokeWidth);// 设置个新的长方形
canvas.drawRoundRect(oval, mStrokeWidth, mStrokeWidth, mLinesPaint);//第二个参数是x半径,第三个参数是y半径
// 画进度条
mLinesPaint1.setColor(mPrimaryColor);
Log.d("RoundLineProgressView", "当前进度=" + mProgress);
int progressEnd = getProgressX(mProgress);
// 计算出进度条的左右边距
float ProgressPadding = (float) (mLinesPaint1.getStrokeWidth() * 0.6);
float startX = ProgressPadding + paddingLeft;
float startY = getProgressTextHeight() + mStrokeWidth / 2;
float endX = progressEnd - ProgressPadding;
float endY = getProgressTextHeight() + mStrokeWidth / 2;
if (startX < endX) {
canvas.drawLine(startX, startY, endX, endY, mLinesPaint1);
} else {
mLinesPaint1.setStyle(Paint.Style.FILL);
canvas.drawCircle(startX, startY, lineWidth / 2, mLinesPaint1);
mLinesPaint1.setStyle(Paint.Style.STROKE);
}
// 画百分比文本
float textX = 0;
if (progressEnd < mStrokeWidth) {
textX = mStrokeWidth / 2 + paddingLeft;
} else if (progressEnd > getWidth() - mStrokeWidth) {
textX = getWidth() - mStrokeWidth / 2 - paddingRight;
} else {
textX = progressEnd;
}
float textY = mBounds.height() << 1;
canvas.drawText(String.valueOf(mProgress), textX, textY, mTextPaint);
}
4.完整代码
public class RoundLineProgressView extends View {
public interface RoundLineProgressChangeListener {
/**
* 当前进度
*/
void onProgressChanged(int progress, int max);
/**
* 进度完成
*/
void onComplete();
}
private static final String TAG = "RoundLineProgressView";
// Paint.ANTI_ALIAS_FLAG 使位图抗锯齿的标志
private final Paint mLinesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mLinesPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RoundLineProgressChangeListener mListener;
private final float m1Dip;
private final float m1Sp;
/**
* 当前进度
*/
private int mProgress;
/**
* 最大值
*/
private int mMax;
/**
* 背景色
*/
private int mPrimaryColor;
/**
* 当前进度的颜色
*/
private int mSecondaryColor;
/**
* 宽度
*/
private float mStrokeWidth;
/**
* 对进度文字的约束
*/
private final Rect mBounds = new Rect();
/**
* 进度条线的宽度
*/
private float lineWidth;
public RoundLineProgressView(Context contex) {
this(contex, null);
}
public RoundLineProgressView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RoundLineProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
m1Dip = getResources().getDisplayMetrics().density;
m1Sp = getResources().getDisplayMetrics().scaledDensity;
int max = 0;
int progress = 0;
float width = dips(8);
int primaryColor = 0xFF009688;
int secondaryColor = 0xFFDADADA;
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RopeProgressBar, defStyleAttr, 0);
if (a != null) {
max = a.getInt(R.styleable.RopeProgressBar_max, max);
progress = a.getInt(R.styleable.RopeProgressBar_progress, progress);
primaryColor = a.getColor(R.styleable.RopeProgressBar_primaryColor, primaryColor);
secondaryColor = a.getColor(R.styleable.RopeProgressBar_secondaryColor, secondaryColor);
width = a.getDimension(R.styleable.RopeProgressBar_progressWidth, width);
a.recycle();
}
mPrimaryColor = primaryColor;
mSecondaryColor = secondaryColor;
mStrokeWidth = width;
// 设置画笔的宽度
mLinesPaint.setStrokeWidth(4);
// 设置画笔的锯齿效果
mLinesPaint.setAntiAlias(true);
// 设置画笔为仅描边
mLinesPaint.setStyle(Paint.Style.STROKE);
lineWidth = (float) (width * 0.8);
mLinesPaint1.setStrokeWidth(lineWidth);
// 设置画笔为仅描边
mLinesPaint1.setStyle(Paint.Style.STROKE);
// 设置画笔笔刷类型
mLinesPaint1.setStrokeCap(Paint.Cap.ROUND);
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(sp(18));
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTypeface(Typeface.create("sans-serif-condensed-light", 0));
setMax(max);
setProgress(progress);
// 硬件加速用的,需要在3.0以上使用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final String progress = String.valueOf(getProgress());
mTextPaint.getTextBounds(progress, 0, progress.length(), mBounds);
final int bubbleHeight = (int) Math.ceil(getBubbleVerticalDisplacement());
int measuredHeight;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
measuredHeight = resolveSizeAndState(bubbleHeight, heightMeasureSpec, 0);
} else {
measuredHeight = resolveSizeAndState2(bubbleHeight, heightMeasureSpec, 0);
}
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), measuredHeight);
}
/**
* 官方的resolveSizeAndState()需要3.0以上版本,所以自己写个resolveSizeAndState(),为了不与系统的发生冲突已resolveSizeAndState2()命名
* @param size
* @param measureSpec
* @param childMeasuredState
* @return
*/
private int resolveSizeAndState2(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
private double getBubbleVerticalDisplacement() {
return getProgressTextMargin() + getProgressTextHeight() + +mStrokeWidth;
}
/**
* 计算出百分比进度文本的高度
* @return
*/
private float getProgressTextHeight() {
return mBounds.height() + dips(16);
}
/**
* 设置一个默认的Margin
* @return
*/
private float getProgressTextMargin() {
return dips(4);
}
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mListener != null) {
if (mProgress == mMax) {
mListener.onComplete();
}
mListener.onProgressChanged(mProgress, mMax);
}
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
// 结束的位置
int end = getWidth() - getPaddingRight();
// 设置底部背景的颜色
mLinesPaint.setColor(mSecondaryColor);
// 画底部的背景
RectF oval = new RectF(paddingLeft, getProgressTextHeight(), end - paddingRight, getProgressTextHeight() + mStrokeWidth);// 设置个新的长方形
canvas.drawRoundRect(oval, mStrokeWidth, mStrokeWidth, mLinesPaint);//第二个参数是x半径,第三个参数是y半径
// 画进度条
mLinesPaint1.setColor(mPrimaryColor);
Log.d("RoundLineProgressView", "当前进度=" + mProgress);
int progressEnd = getProgressX(mProgress);
// 计算出进度条的左右边距
float ProgressPadding = (float) (mLinesPaint1.getStrokeWidth() * 0.6);
float startX = ProgressPadding + paddingLeft;
float startY = getProgressTextHeight() + mStrokeWidth / 2;
float endX = progressEnd - ProgressPadding;
float endY = getProgressTextHeight() + mStrokeWidth / 2;
if (startX < endX) {
canvas.drawLine(startX, startY, endX, endY, mLinesPaint1);
} else {
mLinesPaint1.setStyle(Paint.Style.FILL);
canvas.drawCircle(startX, startY, lineWidth / 2, mLinesPaint1);
mLinesPaint1.setStyle(Paint.Style.STROKE);
}
// 画百分比文本
float textX = 0;
if (progressEnd < mStrokeWidth) {
textX = mStrokeWidth / 2 + paddingLeft;
} else if (progressEnd > getWidth() - mStrokeWidth) {
textX = getWidth() - mStrokeWidth / 2 - paddingRight;
} else {
textX = progressEnd;
}
float textY = mBounds.height() << 1;
canvas.drawText(String.valueOf(mProgress), textX, textY, mTextPaint);
}
/**
* 计算当前进度的X坐标
* @param progress
* @return
*/
private int getProgressX(int progress) {
int progressX = 0;
progressX = (getWidth() - getPaddingLeft() - getPaddingRight()) * progress / mMax;
Log.d(TAG, "当前进度对应的屏幕坐标progressX=" + progressX);
return progressX;
}
public void setMax(int max) {
max = Math.max(0, max);
if (max != mMax) {
mMax = max;
if (mProgress > max) {
mProgress = max;
}
postInvalidate();
}
}
public int getMax() {
return mMax;
}
/**
* 获取当前进度
* @return
*/
public int getProgress() {
return mProgress;
}
public void setOnChangeListener(RoundLineProgressChangeListener mListener) {
this.mListener = mListener;
}
/**
* 设置当前进度
* @param progress
*/
public synchronized void setProgress(int progress) {
Log.d(TAG, "外部内传递过来的进度=" + progress);
if (this.mProgress == progress) {
return;
}
this.mProgress = progress;
postInvalidate();
}
private float dips(final float dips) {
return dips * m1Dip;
}
private float sp(final int sp) {
return sp * m1Sp;
}
}