一、实现效果
二、实现
1.自定义view组成:
1.外圆弧: 属性:宽度、颜色
2.内圆弧 属性:宽度、颜色
3.文字 属性:文字大小、文字颜色
<declare-styleable name="QQStepView">
<!-- 外圆弧颜色-->
<attr name="outerColor" format="color"/>
<!-- 内圆弧颜色-->
<attr name="innerColor" format="color"/>
<!-- 圆弧宽度-->
<attr name="borderWidth" format="dimension"/>
<!-- 文字大小-->
<attr name="stepTextSize" format="dimension"/>
<!-- 文字颜色-->
<attr name="stepTextColor" format="color"/>
</declare-styleable>
2.java代码实现自定义view:
1.获取自定义属性值
2.onMeasure方法确认宽高
3.onDraw绘制view
4.提供方法外部设置当前步数
public class QQStepView extends View {
private static final int START_ANGLE = 135;
private static final int MAX_SWEEP_ANGLE = 270;
private int mOuterColor = Color.RED;
private int mInnerColor = Color.BLACK;
private int mBorderWidth = SizeUtils.dip2px(getContext(), 15); // 20px
private int mStepTextSize = SizeUtils.sp2px(getContext(), 14);
private int mStepTextColor = Color.GREEN;
private Paint mOutPaint, mInnerPaint, mTextPaint;
// 绘制圆弧的范围
private RectF mRectF;
private int mStepMax = 1000;
private int mCurrentStep = 0;
public QQStepView(Context context) {
this(context, null);
}
public QQStepView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public QQStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mOuterColor = array.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
mInnerColor = array.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
// 假如xml设置宽度14dp,这里获取到是转换后的px
mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, mBorderWidth);
mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
// 假如xml设置字体14sp,这里获取到是转换后的px
mStepTextSize = (int) array.getDimension(R.styleable.QQStepView_stepTextSize, mStepTextSize);
array.recycle();
// 1.外圆弧画笔
mOutPaint = new Paint();
mOutPaint.setStrokeWidth(mBorderWidth);
mOutPaint.setColor(mOuterColor);
mOutPaint.setAntiAlias(true);
// 圆角
mOutPaint.setStrokeCap(Paint.Cap.ROUND);
mOutPaint.setStyle(Paint.Style.STROKE);
// 1.内圆弧画笔
mInnerPaint = new Paint();
mInnerPaint.setStrokeWidth(mBorderWidth);
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setAntiAlias(true);
// 圆角
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint.setStyle(Paint.Style.STROKE);
// 3.文字画笔
mTextPaint = new Paint();
mTextPaint.setTextSize(mStepTextSize);
mTextPaint.setColor(mStepTextColor);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int minSize;
if (widthMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.AT_MOST) {
// 只要有一个mode是wrap_content, 则默认宽高是40dp
minSize = SizeUtils.dip2px(getContext(), 40);
} else {
// 宽高不一致
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(widthMeasureSpec);
minSize = Math.min(width, height);
}
setMeasuredDimension(minSize, minSize);
}
@Override
protected void onDraw(Canvas canvas) {
// 画圆弧的区域 (mBorderWidth / 2)的作用是保证圆弧可以显示完全
if (mRectF == null) {
mRectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, getWidth() - mBorderWidth / 2, getHeight() - mBorderWidth / 2);
}
// 1.画外圆弧
canvas.drawArc(mRectF, START_ANGLE, MAX_SWEEP_ANGLE, false, mOutPaint);
// 2.画内圆弧
float sweepAngle = mCurrentStep * 1.0f / mStepMax * MAX_SWEEP_ANGLE;
canvas.drawArc(mRectF, START_ANGLE, sweepAngle, false, mInnerPaint);
// 3.画文字:找基线画文字
// x: 控件宽度/2 - 文字宽度/2
String stepText = mCurrentStep + "";
Rect textBounds = new Rect();
mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);
int dx = getWidth() / 2 - textBounds.width() / 2;
// 基线y
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(stepText, dx, baseLine, mTextPaint);
}
public synchronized void setStepMax(int stepMax) {
mStepMax = stepMax;
}
public synchronized void setCurrentStep(int currentStep) {
mCurrentStep = currentStep;
invalidate();
}
}
3.使用自定义view:
button = findViewById(R.id.btn);
qqStepView = findViewById(R.id.qq_step_view);
qqStepView.setStepMax(4000);
qqStepView.setCurrentStep(1500);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过外部设置步数,以及外部设置不同动画效果,避免写死在自定view内。
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 3000);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
int currentStep = (int) animation.getAnimatedValue();
qqStepView.setCurrentStep(currentStep);
}
});
valueAnimator.start();
}
});