Android弧形步数进度
一、效果图

二、代码实现
2.1 自定义属性
<declare-styleable name="QQStepView">
<attr name="outColor" format="color"/>
<attr name="outWidth" format="dimension"/>
<attr name="innerColor" format="color"/>
<attr name="innerWidth" format="dimension"/>
<attr name="step" format="string"/>
</declare-styleable>
2.2 代码实现
package com.example.layoutdemo.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.Nullable;
import com.example.layoutdemo.R;
public class QQStepView extends View {
private static final String TAG = "QQStepView";
private int mOuterColor;
private int mOuterWidth;
private int mInnerColor;
private int mInnerWidth;
private Paint mOutPaint;
private int maxStep = 100;
private int currentStep = 20;
private Paint mInnerPaint;
private Paint mTextPaint;
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 typedArray = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mOuterColor = typedArray.getColor(R.styleable.QQStepView_outColor,getResources().getColor(R.color.black));
mOuterWidth = typedArray.getDimensionPixelSize(R.styleable.QQStepView_outWidth,10);
mInnerColor = typedArray.getColor(R.styleable.QQStepView_innerColor,getResources().getColor(R.color.design_default_color_on_primary));
mInnerWidth = typedArray.getDimensionPixelSize(R.styleable.QQStepView_innerWidth,5);
typedArray.recycle();
mOutPaint = new Paint();
mOutPaint.setAntiAlias(true);
mOutPaint.setColor(mOuterColor);
mOutPaint.setStyle(Paint.Style.STROKE);
mOutPaint.setStrokeWidth(mOuterWidth);
mOutPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint = new Paint();
mInnerPaint.setAntiAlias(true);
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setStrokeWidth(mInnerWidth);
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint.setStyle(Paint.Style.STROKE);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(getTextSp(40));
}
private float getTextSp(float px) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,px,getResources().getDisplayMetrics());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int size = widthSize < heightSize ? widthSize:heightSize;
setMeasuredDimension(size,size);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rectF = new RectF();
rectF.left = mOuterWidth/2;
rectF.right = getWidth()-mOuterWidth/2;
rectF.top = mOuterWidth/2;
rectF.bottom = getHeight()-mOuterWidth/2;
canvas.drawArc(rectF,135,275,false,mOutPaint);
RectF innerRectF = new RectF();
innerRectF.left = mOuterWidth/2;
innerRectF.right = getWidth()-mOuterWidth/2;
innerRectF.top = mOuterWidth/2;
innerRectF.bottom = getHeight()-mOuterWidth/2;
float sweepAngle = 275 * ((float)currentStep / maxStep);
Log.d(TAG,"sweepAngle"+ sweepAngle);
canvas.drawArc(innerRectF,135,sweepAngle,false,mInnerPaint);
String stepText = currentStep + "";
Rect textRect = new Rect();
mTextPaint.getTextBounds(stepText,0,stepText.length(),textRect);
int dx = getWidth()/2 - textRect.width()/2;
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 setMaxStep(int maxStep){
this.maxStep = maxStep;
}
public synchronized void setCurrentStep(int currentStep) {
this.currentStep = currentStep;
postInvalidate();
}
}
2.3 使用控件
<com.example.layoutdemo.widget.QQStepView
app:outWidth="20dp"
android:id="@+id/step_view"
app:outColor="@color/teal_200"
android:layout_width="200dp"
app:innerColor="@color/black"
app:innerWidth="20dp"
android:layout_height="200dp"/>
final QQStepView qqStepView = findViewById(R.id.step_view);
qqStepView.setMaxStep(3000);
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0,2000);
valueAnimator.setDuration(5000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
qqStepView.setCurrentStep((int)animatedValue);
}
});
valueAnimator.start();
三、源码下载地址
Android自定义View