自定义ArcTextProgressBar
这个控件最终需要实现如下效果:
1、进度条采用半圆弧形式呈现;
2、圆弧上有已完成的进度提示;
见下面效果图:
其他一些诸于设置进度条宽度、颜色,进度文字圆大小等属性具体见代码中的定义,好废话不说,上代码:
首先是自定义的属性,这样便于在xml文件中设置属性:
public class ArcTextProgressBar extends View {
private int diameter = 600; // 圆弧直径
private int mStokeWidth = 40; // 进度条宽度
private int mTextBgCircleDiameter = 20; // 进度条圆形背景上的致敬
private String mTextStringPrefix = "已完成"; // 进度提示文字前缀
private int mMinProgress = 0;
private int mCurrentProgress = 25; // 进度条当前进度
private int mMaxProgress = 100;
private String mTextStringSuffix = "%"; // 进度提示文字后缀
private int mTotalArcColor = Color.GRAY;
private int mCurrentArcColor = Color.CYAN;
private int mCircleTextBgColor = Color.CYAN;
private int mCircleTextColor = Color.WHITE;
private int mCircleTextSize = dipToPx(8);
private int mStartAngle = 180; // 进度条开始绘制的弧度
private int mSweepAngle = 90; // 进度条滑过的弧度
private int mTotalAngle = 180; // 进度条总共的弧度
private Point mPointTextBgCircleCenter = null; // 文字背景圆的圆心坐标
private RectF mRectBg = null; // 背景矩形
private Paint mPaintAllArc = null; //
private Paint mPaintCurrentArc = null;
private Paint mPaintCircleBg = null;
private StaticLayout mStaticLayout = null;
private TextPaint mPaintCircleText = null;
public ArcTextProgressBar(Context context) {
super(context, null);
initView();
}
public ArcTextProgressBar(Context context, AttributeSet attrs) {
super(context, attrs, 0);
initView();
initConfig(context, attrs);
}
public ArcTextProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
initConfig(context, attrs);
}
private void initConfig(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcTextProgressBar);
mStokeWidth = typedArray.getInteger(R.styleable.ArcTextProgressBar_arc_text_progress_stroke_width, 40);
mTextBgCircleDiameter = typedArray.getInteger(R.styleable.ArcTextProgressBar_arc_text_progress_text_bg_circle_diameter, 20);
mTextStringPrefix = typedArray.getString(R.styleable.ArcTextProgressBar_arc_text_progress_text_string_prefix);
mTextStringSuffix = typedArray.getString(R.styleable.ArcTextProgressBar_arc_text_progress_text_string_suffix);
mMinProgress = typedArray.getInteger(R.styleable.ArcTextProgressBar_arc_text_progress_min_progress, 0);
mMaxProgress = typedArray.getInteger(R.styleable.ArcTextProgressBar_arc_text_progress_max_progress, 100);
mCurrentProgress = typedArray.getInteger(R.styleable.ArcTextProgressBar_arc_text_progress_current_progress, 25);
mTotalArcColor = typedArray.getColor(R.styleable.ArcTextProgressBar_arc_text_progress_total_arc_color, Color.GRAY);
mCurrentArcColor = typedArray.getColor(R.styleable.ArcTextProgressBar_arc_text_progress_current_arc_color, Color.CYAN);
mCircleTextBgColor = typedArray.getColor(R.styleable.ArcTextProgressBar_arc_text_progress_circle_text_bg_color, Color.CYAN);
mCircleTextColor = typedArray.getColor(R.styleable.ArcTextProgressBar_arc_text_progress_circle_text_color, Color.WHITE);
mCircleTextSize = typedArray.getInteger(R.styleable.ArcTextProgressBar_arc_text_progress_circle_text_size, dipToPx(8));
// 进行初始化设置
if (null == mTextStringPrefix) {
mTextStringPrefix = "已完成";
}
if (null == mTextStringSuffix) {
mTextStringSuffix = "%";
}
setStokeWidth(mStokeWidth);
setTextBgCircleDiameter(mTextBgCircleDiameter);
setTextStringPrefix(mTextStringPrefix);
setTextStringSuffix(mTextStringSuffix);
setMinProgress(mMinProgress);
setMaxProgress(mMaxProgress);
setCurrentProgress(mCurrentProgress);
setTotalArcColor(mTotalArcColor);
setCurrentArcColor(mCurrentArcColor);
setCircleTextBgColor(mCircleTextBgColor);
setCircleTextColor(mCircleTextColor);
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = Math.max(mTextBgCircleDiameter, mStaticLayout.getWidth()) + 10 + diameter;
int height = Math.max(mTextBgCircleDiameter, mStaticLayout.getWidth()) + 10 + diameter / 2;
setMeasuredDimension(width, height);
}
private void initView() {
diameter = getScreenWidth() * 4 / 5 - getPaddingLeft() - getPaddingRight();
// 绘制文字
mPaintCircleText = new TextPaint();
mPaintCircleText.setColor(mCircleTextColor);
mPaintCircleText.setAntiAlias(true);
mPaintCircleText.setTextSize(mCircleTextSize);
float startChangeLinePos = mPaintCircleText.measureText(mTextStringPrefix);
mStaticLayout = new StaticLayout(mTextStringPrefix + mCurrentProgress + mTextStringSuffix,
mPaintCircleText, (int) startChangeLinePos, Layout.Alignment.ALIGN_CENTER, 1.0f, 1.0f, true);
int maxStroke = mStokeWidth + mTextBgCircleDiameter + mStaticLayout.getWidth();
// 计算当前滑过的弧度
mSweepAngle = (int) (mCurrentProgress * 1.0 / 100 * mTotalAngle);
mRectBg = new RectF();
mRectBg.left = Math.max(mTextBgCircleDiameter, mStaticLayout.getWidth()) / 2 + 10;
mRectBg.top = Math.max(mTextBgCircleDiameter, mStaticLayout.getWidth()) / 2 + 10;
mRectBg.right = Math.max(mTextBgCircleDiameter, mStaticLayout.getWidth()) / 2 + 10 + diameter;
mRectBg.bottom = Math.max(mTextBgCircleDiameter, mStaticLayout.getWidth()) / 2 + 10 + diameter;
// 绘制全部进度圆弧
mPaintAllArc = new Paint();
mPaintAllArc.setAntiAlias(true);
mPaintAllArc.setStyle(Paint.Style.STROKE);
mPaintAllArc.setStrokeWidth(mStokeWidth);
mPaintAllArc.setColor(mTotalArcColor);
mPaintAllArc.setStrokeCap(Paint.Cap.ROUND);
// 当前进度的弧形
mPaintCurrentArc = new Paint();
mPaintCurrentArc.setAntiAlias(true);
mPaintCurrentArc.setStyle(Paint.Style.STROKE);
mPaintCurrentArc.setStrokeCap(Paint.Cap.ROUND);
mPaintCurrentArc.setStrokeWidth(mStokeWidth);
mPaintCurrentArc.setColor(mCurrentArcColor);
// 绘制当前文字圆形背景
mPaintCircleBg = new Paint();
mPaintCircleBg.setAntiAlias(true);
mPaintCircleBg.setStyle(Paint.Style.FILL);
mPaintCircleBg.setColor(mCircleTextBgColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 控件背景
// canvas.drawRect(mRectBg, mPaintRectBg);
// 整个弧
canvas.drawArc(mRectBg, mStartAngle, mTotalAngle, false, mPaintAllArc);
// 当前进度
canvas.drawArc(mRectBg, mStartAngle, mSweepAngle, false, mPaintCurrentArc);
// 得到文字背景圆的圆心
mPointTextBgCircleCenter = calculateTextBgCircleCenter();
if (mCurrentProgress == 0 || mCurrentProgress == 100) {
canvas.drawCircle(mPointTextBgCircleCenter.x, mPointTextBgCircleCenter.y - mStaticLayout.getWidth() / 2,
((Math.max(mStokeWidth, mTextBgCircleDiameter) + mStaticLayout.getWidth()) / 2), mPaintCircleBg);
//开始绘制文字的位置
canvas.translate(mPointTextBgCircleCenter.x - mStaticLayout.getWidth() / 2,
mPointTextBgCircleCenter.y - mStaticLayout.getHeight());
} else {
canvas.drawCircle(mPointTextBgCircleCenter.x, mPointTextBgCircleCenter.y,
((Math.max(mStokeWidth, mTextBgCircleDiameter) + mStaticLayout.getWidth()) / 2), mPaintCircleBg);
//开始绘制文字的位置
canvas.translate(mPointTextBgCircleCenter.x - mStaticLayout.getWidth() / 2,
mPointTextBgCircleCenter.y - mStaticLayout.getHeight() / 2);
}
mStaticLayout.draw(canvas);
}
/**
* 关键点:计算文字背景圆的圆心
*
* @return the textBgCircleCenter
*/
private Point calculateTextBgCircleCenter() {
// 得到圆环中心圆对应的半径: 外半径 - 内半径 / 2
float arcRadius = mRectBg.width() / 2;
//圆心
Point point = new Point();
point.x = (int) (mRectBg.width() / 2 + mRectBg.left - arcRadius * Math.cos(mSweepAngle * 3.14 / 180));
point.y = (int) (mRectBg.height() / 2 + mRectBg.top - arcRadius * Math.sin(mSweepAngle * 3.14 / 180));
return point;
}
/**
* dip 转换成px
*
* @param dip
* @return
*/
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
/**
* 得到屏幕宽度
*
* @return
*/
private int getScreenWidth() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.widthPixels;
}
/**
* 设置progressBar宽度
*
* @param stokeWidth 宽度
*/
public void setStokeWidth(int stokeWidth) {
this.mStokeWidth = stokeWidth;
mPaintAllArc.setStrokeWidth(mStokeWidth);
mPaintCurrentArc.setStrokeWidth(mStokeWidth);
invalidate();
}
/**
* 设置progressBar进度提示圆形直径
*
* @param textBgCircleDiameter 直径
**/
public void setTextBgCircleDiameter(int textBgCircleDiameter) {
this.mTextBgCircleDiameter = textBgCircleDiameter;
// todo 根据直径大小来设置里面文字的大小
invalidate();
}
public void setTextStringPrefix(String textStringPrefix) {
this.mTextStringPrefix = textStringPrefix;
invalidate();
}
public void setMinProgress(int minProgress) {
this.mMinProgress = minProgress;
}
public void setCurrentProgress(int currentProgress) {
if (currentProgress < mMinProgress) {
currentProgress = mCurrentProgress;
} else if (currentProgress > mMaxProgress) {
currentProgress = mMaxProgress;
}
this.mCurrentProgress = currentProgress;
mSweepAngle = (int) (mCurrentProgress * 1.0 / 100 * mTotalAngle);
float startChangeLinePos = mPaintCircleText.measureText(mTextStringPrefix);
mStaticLayout = new StaticLayout(mTextStringPrefix + mCurrentProgress + mTextStringSuffix,
mPaintCircleText, (int) startChangeLinePos, Layout.Alignment.ALIGN_CENTER, 1.0f, 1.0f, true);
// todo 设置动画
invalidate();
}
public void setMaxProgress(int maxProgress) {
this.mMaxProgress = maxProgress;
}
public void setTextStringSuffix(String textStringSuffix) {
this.mTextStringSuffix = textStringSuffix;
invalidate();
}
public void setTotalArcColor(int mTotalArcColor) {
this.mTotalArcColor = mTotalArcColor;
mPaintAllArc.setColor(mTotalArcColor);
invalidate();
}
public void setCurrentArcColor(int currentArcColor) {
this.mCurrentArcColor = currentArcColor;
mPaintCurrentArc.setColor(mCurrentArcColor);
invalidate();
}
public void setCircleTextBgColor(int circleTextBgColor) {
this.mCircleTextBgColor = circleTextBgColor;
mPaintCircleBg.setColor(mCircleTextBgColor);
invalidate();
}
public void setCircleTextColor(int circleTextColor) {
this.mCircleTextColor = circleTextColor;
mPaintCircleText.setColor(mCircleTextColor);
invalidate();
}
public void setCircleTextSize(int circleTextSize) {
this.mCircleTextSize = dipToPx(circleTextSize);
mPaintCircleText.setTextSize(mCircleTextSize);
invalidate();
}
}
1、进度圆圆心的确定;
2、onMeasure整个空间宽、高的计算;
需要完善的地方:
1、进度圆中的文字大小应该能自适应圆本身的大小;
2、添加进度圆动画效果;
将在之后进行更新。