一、概述
本文详细结合自定义 View 和 属性动画,讲述如何自定义一个圆弧计步器。
二、实现步骤分析
- 确定自定义属性,编写attrs.xml
- 在自定义View中获取自定义属性,做好初始化工作
- onMeasure(int widthMeasureSpec, int heightMeasureSpec) 确保正方形
- onDraw(Canvas canvas) 画外圆弧、内圆弧、文字
- 提供调用方法
三、具体实现
- 确定自定义属性,编写attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="StepView">
<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>
- 在自定义View中获取自定义属性,做好初始化工作
public StepView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.StepView);
mOuterColor = array.getColor(R.styleable.StepView_outerColor,mOuterColor);
mInnerColor = array.getColor(R.styleable.StepView_innerColor, mInnerColor);
mBorderWidth = (int) array.getDimension(R.styleable.StepView_borderWidth,mBorderWidth);
mStepTextSize = array.getDimensionPixelSize(R.styleable.StepView_stepTextSize,mStepTextSize);
mStepTextColor = array.getColor(R.styleable.StepView_stepTextColor, mStepTextColor);
array.recycle();
mOutPaint = new Paint();
mOutPaint.setAntiAlias(true);
mOutPaint.setStrokeWidth(mBorderWidth);
mOutPaint.setColor(mOuterColor);
mOutPaint.setStrokeCap(Paint.Cap.ROUND);
mOutPaint.setStyle(Paint.Style.STROKE); // 画笔空心
mInnerPaint = new Paint();
mInnerPaint.setAntiAlias(true);
mInnerPaint.setStrokeWidth(mBorderWidth);
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint.setStyle(Paint.Style.STROKE); // 画笔空心
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(mStepTextColor);
mTextPaint.setTextSize(mStepTextSize);
// 宽高默认 30 dp
width = dp2px(context, 30);
height = dp2px(context, 30);
}
- onMeasure(int widthMeasureSpec, int heightMeasureSpec) 确保正方形
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 调用者在布局文件中可能 wrap_content
// 获取模式 AT_MOST 默认30 dp
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode != MeasureSpec.AT_MOST){
width = MeasureSpec.getSize(widthMeasureSpec);
}
if (heightMode != MeasureSpec.AT_MOST){
height = MeasureSpec.getSize(heightMeasureSpec);
}
// 宽度高度不一致 取最小值,确保是个正方形
setMeasuredDimension(width > height ? height : width, width > height ? height : width);
}
- onDraw(Canvas canvas) 画外圆弧、内圆弧、文字
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// int center = getWidth() / 2;
// int radius = getWidth() / 2 - mBorderWidth / 2;
// RectF rectF = new RectF(center - radius,center - radius ,center + radius,center + radius);
// 下面代码由上面代码简化而成
RectF rectF = new RectF(mBorderWidth / 2 + getPaddingLeft(),mBorderWidth / 2 + getPaddingTop()
,getWidth() - (mBorderWidth / 2 + getPaddingRight()),getHeight() - (mBorderWidth / 2 + getPaddingBottom()));
canvas.drawArc(rectF,135,270,false,mOutPaint);
if(mStepMax == 0) return;
// 画内圆弧 怎么画肯定不能写死 百分比 是使用者设置的从外面传
float sweepAngle = (float)mCurrentStep / mStepMax;
canvas.drawArc(rectF, 135, sweepAngle * 270, false, mInnerPaint);
// 画文字
String stepText = mCurrentStep+"";
Rect textBounds = new Rect();
mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);
int dx = getWidth() / 2 - textBounds.width() / 2;
// 基线 baseLine
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
int dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(stepText,dx,baseLine,mTextPaint);
}
- 提供调用方法
/**
* 设置步数最大值
* @param stepMax
*/
public synchronized void setStepMax(int stepMax){
this.mStepMax = stepMax;
}
/**
* 设置当前步数
* @param currentStep
*/
public synchronized void setCurrentStep(int currentStep){
this.mCurrentStep = currentStep;
// 刷新
invalidate();
}
四、结合属性动画使用
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.vegen.StepView
android:id="@+id/step_view"
app:outerColor="@color/colorPrimary"
app:innerColor="@color/colorAccent"
app:borderWidth="20dp"
app:stepTextColor="@color/colorAccent"
app:stepTextSize="30sp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Activity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final StepView qqStepView = (StepView) findViewById(R.id.step_view);
qqStepView.setStepMax(10000);
// 属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 8000);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();
qqStepView.setCurrentStep((int)currentStep);
}
});
valueAnimator.start();
}
}
网友评论