自定义 ProgressBar
自定义一个ProgressBar,按照自定义View的流程来进行。
因为我们只是需要做一个外表比系统控件更酷炫的progressBar,它的功能不会发生改变,所以我们创建一个HorizontalProgress类,让它继承ProgressBar,这样我们就不用自己去实现一些ProgressBar本身需要的方法了。
第一步:自定义属性的声明和获取
按照想要实现的自定义progressBar的效果,我们可以确定我们需要的自定义属性:
- 未完成进度条颜色(文字右侧)
- 未完成进度条高度
- 已完成进度条颜色(文字左侧)
- 已完成进度条高度
- 字体的大小
- 字体颜色
- 字体与两侧进度条的间距
基本上就是这些,然后在res/values/attrs.xml中
<resources>
<declare-styleable name="HorizontalProgress">
//未完成进度条颜色
<attr name="progress_unreach_color" format="color" />
//未完成进度条高度
<attr name="progress_unreach_height" format="dimension" />
//已完成进度条颜色
<attr name="progress_reach_color" format="color" />
//已完成进度条高度
<attr name="progress_reach_height" format="dimension" />
//字体颜色
<attr name="progress_text_color" format="color" />
//字体大小
<attr name="progress_text_size" format="dimension" />
//文字与进度条的间距
<attr name="progress_text_offset" format="dimension" />
</declare-styleable>
</resources>
接下来就要在Java代码中获取自定义属性。
private void getStyleAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.HorizontalProgress);
mTextSize = (int) typedArray.getDimension(R.styleable.HorizontalProgress_progress_text_size, mTextSize);
mTextColor = typedArray.getColor(R.styleable.HorizontalProgress_progress_text_color, mTextColor);
mTextOffset = (int) typedArray.getDimension(R.styleable.HorizontalProgress_progress_text_offset, mTextOffset);
mUnReachColor = typedArray.getColor(R.styleable.HorizontalProgress_progress_unreach_color, mUnReachColor);
mUnReachHeight = (int) typedArray.getDimension(R.styleable.HorizontalProgress_progress_unreach_height, mUnReachHeight);
mReachColor = typedArray.getColor(R.styleable.HorizontalProgress_progress_reach_color, mReachColor);
mReachHeight = (int) typedArray.getDimension(R.styleable.HorizontalProgress_progress_reach_height, mReachHeight);
typedArray.recycle();
mPaint.setTextSize(mTextSize);
}
现在我们就用一些全局变量保存了View的属性了。
为了避免在设置属性的时候可能会有部分属性用户没有用到,所以上面的全局变量在初始化的时候就赋予了初始值。
第二步:onMeasure 测量
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//宽度,进度条不支持wrap_content,必须给一个确定的宽度,所以不用像高度一样去进行判断
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthValue = MeasureSpec.getSize(widthMeasureSpec);
//判断高度
int heightValue = measurHeight(heightMeasureSpec);
//确定了宽和高
setMeasuredDimension(widthValue, heightValue);
//真正的progressBar显示的长度
mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
//对于三种模式的处理过程
private int measurHeight(int heightMeasureSpec) {
int result = 0;
int mode = MeasureSpec.getMode(heightMeasureSpec);
int value = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = value;
} else {
//获取字体的高度
int textHeight = (int) (mPaint.descent() - mPaint.ascent());
result = getPaddingTop() + getPaddingBottom() + Math.max(Math.max(mReachHeight, mUnReachHeight), Math.abs(textHeight));
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, value);
}
}
return result;
}
第三步:onDraw 绘制
由于自定义ProgressBar不是一个ViewGroup,不需要使用layout。
@Override
protected synchronized void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(getPaddingLeft(), getHeight() / 2);
//draw Reach Bar(文字左侧bar)
//如果已绘制长度加上文本宽度加上offSet宽度大于了mRealWidth的时候就不需要再去画未绘制区域了
boolean noNeedUnReach = false;
//获取字体的长度
String text = getProgress() + "%";
int textWidth = (int) mPaint.measureText(text);
float radio = getProgress() * 1.0f / getMax();
float progressX = radio * mRealWidth;
if (progressX + textWidth > mRealWidth) {
//重置,保证文字显示正确
progressX = mRealWidth - textWidth;
noNeedUnReach = true;
}
float endX = progressX - mTextOffset / 2;
if (endX > 0) {
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mReachHeight);
canvas.drawLine(0, 0, endX, 0, mPaint);
}
//draw text
mPaint.setColor(mTextColor);
int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
canvas.drawText(text, progressX, y, mPaint);
//draw unReachBar(文字右侧bar)
if (!noNeedUnReach) {
float start = progressX + mTextOffset / 2 + textWidth;
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mUnReachHeight);
canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
}
canvas.restore();
}
最后使用
<com.david.progressbar.view.HorizontalProgress
android:id="@+id/progress1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="35dp"
android:progress="0"
david:progress_reach_color="@color/colorAccent"
david:progress_text_color="@color/colorPrimaryDark"
david:progress_unreach_color="@color/colorPrimary" />
圆形ProgressBar
圆形ProgressBar可以继承HorizontalProgress,然后重写onMeasure和onDraw方法。
同时要添加一个圆半径的自定义属性.
具体代码请查看 ProgressBar