项目GitHub地址
思路
- 固定不动的蓝色大圆弧
- 动画变动的红色小圆弧
- 中间的步数文字显示
- 相关的
自定义属性
比如固定不动的大圆弧
, 我们不能写死他的蓝色颜色属性, 要提供一个颜色的自定义属性
给用户自定义配置
;圆弧的宽度
也是要可以自定义的;
自定义属性
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<resources>
...
<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>
</resources>
布局使用
代码语言:javascript复制<com.lwp.customviewtest.CustomViews.QQStepView
android:id="@ id/step_view"
app:outerColor="@color/colorPrimary"
app:innerColor="@color/colorAccent"
app:borderWidth="15dp"
app:stepTextColor="@color/colorAccent"
app:stepTextSize="16sp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
自定义View逻辑
代码语言:javascript复制public class QQStepView extends View {
//初始化 自定义属性变量 并给 默认值
private int mOuterColor = Color.RED;//默认红色
private int mInnerColor = Color.BLUE;//默认蓝色
private int mBorderWidth = 20;//20px
private int mStepTextSize = 20;
private int mStepTextColor = Color.RED;
private Paint mPaint;
private int mStartAngle = 135;
private int mSweepAngle = 270;
private int mStepMax = 0;
private int mCurrentStep = 0;
public QQStepView(Context context) {
this(context, null);
}
public QQStepView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QQStepView(Context context, 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);
mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, mBorderWidth);
mStepTextSize = array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, mStepTextSize);
mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
array.recycle();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setColor(mOuterColor);
mPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 调用者在布局文件中可能是 wrap_content 要做判断
//获取宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//设置宽高
// 宽高不一致 取最小值、确保是个正方形
setMeasuredDimension(widthSize > heightSize ? heightSize : widthSize,
widthSize > heightSize ? heightSize : widthSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画外圆弧==============================
//拿到View的中心点
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 半径
int radius = (int) (centerX - mBorderWidth);
// 设置圆弧画笔的宽度
mPaint.setStrokeWidth(mBorderWidth);
// 设置弧线的 首端 和 尾端 为 圆弧
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
// 设置画笔颜色
mPaint.setColor(mOuterColor);
mPaint.setStyle(Paint.Style.STROKE);
//圆弧 内边缘的弧线的 外切矩形
RectF oval = new RectF(mBorderWidth, mBorderWidth, centerX radius, centerY radius);
// 画背景圆弧
canvas.drawArc(oval, mStartAngle, mSweepAngle, false, mPaint);
//画内圆弧==============================
// 提供百分比 给用户自己配置 【自定义属性】
mPaint.setColor(mInnerColor);
// 计算当前百分比
float percent = (float) mCurrentStep / mStepMax;
if (mStepMax != 0) {
// 根据当前百分比计算圆弧扫描的角度
canvas.drawArc(oval, mStartAngle, percent * mSweepAngle, false, mPaint);
}
//画文字==============================
// 重置画笔
mPaint.reset();
mPaint.setAntiAlias(true);
mPaint.setTextSize(mStepTextSize);
mPaint.setColor(mStepTextColor);
//文本内容
// String mStep = ((int) (percent * mStepMax)) "";
String mStep = mCurrentStep "";
// 测量文字的宽高
Rect textBounds = new Rect();
mPaint.getTextBounds(mStep, 0, mStep.length(), textBounds);
//文字的x轴起始点
int dx = (getWidth() - textBounds.width()) / 2;
// 获取画笔的FontMetrics
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
// 计算文字的基线
int baseLine = (int) (getHeight() / 2 (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
// 绘制步数文字
canvas.drawText(mStep, dx, baseLine, mPaint);
}
// 设置当前最大步数
public synchronized void setMaxStep(int maxStep) {
if (maxStep < 0) {
throw new IllegalArgumentException("max 不能小于0!");
}
this.mStepMax = maxStep;
}
public synchronized int getMaxStep() {
return mStepMax;
}
// 设置进度
public synchronized void setProgress(int progress) {
if (progress < 0) {
throw new IllegalArgumentException("progress 不能小于0!");
}
this.mCurrentStep = progress;
// 重新刷新绘制 -> onDraw()
invalidate();
}
public synchronized int getProgress() {
return mCurrentStep;
}
}
在Activity中使用自定义View和动画
代码语言:javascript复制final QQStepView qqStepView = (QQStepView) findViewById(R.id.step_view);
qqStepView.setMaxStep(6000);
//属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 5678);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();
qqStepView.setProgress((int)currentStep);
}
});
valueAnimator.start();
break;
效果:
参考:自定义View - 仿QQ运动步数进度效果