自定义view 定制一个带比例的环形进度控件,效果图如下:
大致思路如下:
- 最外层的圆弧
- 内层显示进度的圆弧
- view内文字显示
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 中心坐标
*/
float mCenterX = getWidth() / 2;
/**
* 圆弧外轮廓 矩形区域
*/
RectF mRectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2);
//画背景圆弧
drawArcOne(canvas, mRectF);
//画进度圆弧
drawArcTwo(canvas, mRectF);
//画中间数字
drawTextNum(canvas, mCenterX);
//画中间文字
drawText(canvas, mCenterX);
}
最外层的圆弧,绘制开始:
/**
* 绘制最外层圆弧
* @param canvas
* @param mRectF
*/
private void drawArcOne(Canvas canvas, RectF mRectF) {
Paint paint = new Paint();
//画笔颜色
paint.setColor(Color.parseColor("#e0f9ff"));
//画笔结合处类型 圆形
paint.setStrokeJoin(Paint.Join.ROUND);
//护臂落笔出类型 圆形
paint.setStrokeCap(Paint.Cap.ROUND);
//画笔类型 设置为 仅填充边框
paint.setStyle(Paint.Style.STROKE);
//设置抗锯齿
paint.setAntiAlias(true);
//设置边框宽度
paint.setStrokeWidth(mBorderWidth);
/**
* mRectF 圆形外矩形
* mStartAngle 起始角度
* mEndAngle 结束角度
*/
canvas.drawArc(mRectF, mStartAngle, mEndAngle, false, paint);
}
绘制内层进度圆弧
/**
* 绘制内层进度圆弧
* @param canvas
* @param mRectF
*/
private void drawArcTwo(Canvas canvas, RectF mRectF) {
Paint paint = new Paint();
//画笔颜色
paint.setColor(getResources().getColor(R.color.blue_light));
//画笔宽度
paint.setStrokeWidth(mBorderWidth);
//画笔抗锯齿
paint.setAntiAlias(true);
//Paint.Style.STROKE 仅填充描边
paint.setStyle(Paint.Style.STROKE);
//画笔触笔类型 Paint.Cap.ROUND 圆形
paint.setStrokeCap(Paint.Cap.ROUND);
//结合处为 Paint.Join.ROUND 圆形
paint.setStrokeJoin(Paint.Join.ROUND);
canvas.drawArc(mRectF, mStartAngle, mCurrentAngle, false, paint);
}
绘制内层数字
/**
* 绘制内层数字
* @param canvas
* @param centerX
*/
private void drawTextNum(Canvas canvas, float centerX) {
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
paint.setTextSize(mTextSize);
paint.setColor(getResources().getColor(R.color.black));
//字体类型
Typeface mTypeFace = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
paint.setTypeface(mTypeFace);
Rect rect = new Rect();
paint.getTextBounds(mStepNum, 0, mStepNum.length(), rect);
//获取文字基线位置
float biseY = getTextBaseline(paint, getHeight() / 2);
//为了显示数字之下的文字,在基线坐标基础上减去文字高度的一半
canvas.drawText(mStepNum, centerX, biseY - rect.height() / 2, paint);
}
绘制数字下的文字
/**
* 绘制文字
* @param canvas
* @param mCenterX
*/
private void drawText(Canvas canvas, float mCenterX) {
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setColor(getResources().getColor(R.color.black));
paint.setAntiAlias(true);
paint.setTextSize(dipToPx(18));
String s = "步数";
Rect rect = new Rect();
paint.getTextBounds(s, 0, 2, rect);
float biseY = getTextBaseline(paint, getHeight() / 2);
canvas.drawText(s, mCenterX, biseY + getFontHeight(mTextSize), paint);
}
对外的方法
/**
* 对外的方法
* @param mTotalNum 总数
* @param mCurrentStepNum 当前数
*/
public void setCurrentStep(int mTotalNum, int mCurrentStepNum) {
if (mCurrentStepNum > mTotalNum) {
mCurrentStepNum = mTotalNum;
}
//上次步数所占圆环比例
float scalePrevious = (float) Integer.parseInt(mStepNum) / mTotalNum;
float previousAngle = scalePrevious * mEndAngle;
// 所走步数所占总数的百分比
float scale = (float) mCurrentStepNum / mTotalNum;
float currentAngle = scale * mEndAngle;
mStepNum = String.valueOf(mCurrentStepNum);
setTextSize(mCurrentStepNum);
//执行动画
startAnimation(previousAngle,currentAngle,animationLength);
}
属性动画
private void startAnimation(float start, float current, int length) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(start,current);
valueAnimator.setDuration(length);
valueAnimator.setTarget(mCurrentAngle);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentAngle = (float) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
下面附上完整代码,望各位大佬指正
//圆弧自定义view
public class ArcView extends View {
/**
* 圆弧的宽度
*
* @param context
*/
private float mBorderWidth = dipToPx(10);
/**
* 中心数字的大小
*
* @param context
*/
private float mTextSize = dipToPx(22);
/**
* 背景圆的起始角度
*
* @param context
*/
private float mStartAngle = 90;
/**
* 背景圆的结束角度
*
* @param context
*/
private float mEndAngle = 360;
/**
* 进度夹角
*
* @param context
*/
private float mCurrentAngle = 0;
/**
* 步数
*/
private String mStepNum = "0";
/**
* 动画时长
*/
private int animationLength = 1000;
public ArcView(Context context) {
super(context);
}
public ArcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 中心坐标
*/
float mCenterX = getWidth() / 2;
/**
* 圆弧外轮廓 矩形区域
*/
RectF mRectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2);
//画背景圆弧
drawArcOne(canvas, mRectF);
//画进度圆弧
drawArcTwo(canvas, mRectF);
//画中间数字
drawTextNum(canvas, mCenterX);
//画中间文字
drawText(canvas, mCenterX);
}
/**
* 绘制文字
* @param canvas
* @param mCenterX
*/
private void drawText(Canvas canvas, float mCenterX) {
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setColor(getResources().getColor(R.color.black));
paint.setAntiAlias(true);
paint.setTextSize(dipToPx(18));
String s = "步数";
Rect rect = new Rect();
paint.getTextBounds(s, 0, 2, rect);
float biseY = getTextBaseline(paint, getHeight() / 2);
canvas.drawText(s, mCenterX, biseY + getFontHeight(mTextSize), paint);
}
/**
* 获取当前步数的数字的高度
*
* @param fontSize 字体大小
* @return 字体高度
*/
public int getFontHeight(float fontSize) {
Paint paint = new Paint();
paint.setTextSize(fontSize);
Rect bounds_Number = new Rect();
paint.getTextBounds(mStepNum, 0, mStepNum.length(), bounds_Number);
return bounds_Number.height();
}
/**
* 绘制内层数字
* @param canvas
* @param centerX
*/
private void drawTextNum(Canvas canvas, float centerX) {
Paint paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
paint.setTextSize(mTextSize);
paint.setColor(getResources().getColor(R.color.black));
//字体类型
Typeface mTypeFace = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
paint.setTypeface(mTypeFace);
Rect rect = new Rect();
paint.getTextBounds(mStepNum, 0, mStepNum.length(), rect);
//获取文字基线位置
float biseY = getTextBaseline(paint, getHeight() / 2);
//为了显示数字之下的文字,在基线坐标基础上减去文字高度的一半
canvas.drawText(mStepNum, centerX, biseY - rect.height() / 2, paint);
}
/**
* 绘制内层进度圆弧
* @param canvas
* @param mRectF
*/
private void drawArcTwo(Canvas canvas, RectF mRectF) {
Paint paint = new Paint();
//画笔颜色
paint.setColor(getResources().getColor(R.color.blue_light));
//画笔宽度
paint.setStrokeWidth(mBorderWidth);
//画笔抗锯齿
paint.setAntiAlias(true);
//Paint.Style.STROKE 仅填充描边
paint.setStyle(Paint.Style.STROKE);
//画笔触笔类型 Paint.Cap.ROUND 圆形
paint.setStrokeCap(Paint.Cap.ROUND);
//结合处为 Paint.Join.ROUND 圆形
paint.setStrokeJoin(Paint.Join.ROUND);
canvas.drawArc(mRectF, mStartAngle, mCurrentAngle, false, paint);
}
/**
* 绘制最外层圆弧
* @param canvas
* @param mRectF
*/
private void drawArcOne(Canvas canvas, RectF mRectF) {
Paint paint = new Paint();
//画笔颜色
paint.setColor(Color.parseColor("#e0f9ff"));
//画笔结合处类型 圆形
paint.setStrokeJoin(Paint.Join.ROUND);
//护臂落笔出类型 圆形
paint.setStrokeCap(Paint.Cap.ROUND);
//画笔类型 设置为 仅填充边框
paint.setStyle(Paint.Style.STROKE);
//设置抗锯齿
paint.setAntiAlias(true);
//设置边框宽度
paint.setStrokeWidth(mBorderWidth);
/**
* mRectF 圆形外矩形
* mStartAngle 起始角度
* mEndAngle 结束角度
*/
canvas.drawArc(mRectF, mStartAngle, mEndAngle, false, paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 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));
}
/**
* 计算绘制文本的基线
*
* @param paint 绘制文字的画笔
* @param halfHeight 高度的一半
*/
private float getTextBaseline(Paint paint, float halfHeight) {
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
return halfHeight + dy;
}
/**
* 对外的方法
* @param mTotalNum 总数
* @param mCurrentStepNum 当前数
*/
public void setCurrentStep(int mTotalNum, int mCurrentStepNum) {
if (mCurrentStepNum > mTotalNum) {
mCurrentStepNum = mTotalNum;
}
//上次步数所占圆环比例
float scalePrevious = (float) Integer.parseInt(mStepNum) / mTotalNum;
float previousAngle = scalePrevious * mEndAngle;
// 所走步数所占总数的百分比
float scale = (float) mCurrentStepNum / mTotalNum;
float currentAngle = scale * mEndAngle;
mStepNum = String.valueOf(mCurrentStepNum);
setTextSize(mCurrentStepNum);
//执行动画
startAnimation(previousAngle,currentAngle,animationLength);
}
private void startAnimation(float start, float current, int length) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(start,current);
valueAnimator.setDuration(length);
valueAnimator.setTarget(mCurrentAngle);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentAngle = (float) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
/**
* 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置
*
* @param num
*/
public void setTextSize(int num) {
String s = String.valueOf(num);
int length = s.length();
if (length <= 4) {
mTextSize = dipToPx(30);
} else if (length > 4 && length <= 6) {
mTextSize = dipToPx(25);
} else if (length > 6 && length <= 8) {
mTextSize = dipToPx(24);
} else if (length > 8) {
mTextSize = dipToPx(22);
}
}
}