转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/53445072
本文出自:【顾林海的博客】
##前言
最近看到一个评分和计步的弧形控件,于是自己也做了一个,希望大家按照这种思路可以写出更好看的控件。
##用法
public class MainActivity extends AppCompatActivity {
private RingRotateView rrv;
private Button btn_start;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initView() {
btn_start = (Button) findViewById(R.id.btn_start);
rrv = (RingRotateView) findViewById(R.id.rrv);
}
private void initEvent() {
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
rrv.setScore(90, 100);
rrv.startAnimation(new DecelerateInterpolator());
}
});
rrv.setOnScoreListener(new RingRotateView.OnScoreListener() {
@Override
public void finish() {
Toast.makeText(MainActivity.this, "评测完毕", Toast.LENGTH_SHORT).show();
}
});
}
}
##知识点
1、ValueAnimator
ValueAnimator是属性动画的核心类。它实现的就是为动画的初始值和结束值在动画运行时间内提供一个平滑的过度。以上会用到ValueAnimator的ofObject方法。
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
ofObject方法除了传递初始值和结束值之外,还需要传递一个TypeEvaluator类型的参数,由于ofObject方法的初始值和结束值都是自定义的对象,而ValueAnimator并不知道如何从初始的对象过度到结束的对象,所以需要调用者制定一个过度的规则。
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
return object3;
}
}, object1, object2);
传入了TypeEvaluator的实现类并重写了evaluate()方法。evaluate()方法中的第一个参数fraction就表示了动画的当前进度,范围是0到1,我们就根据这个进度值来计算整个动画过渡的值。object1、object2分别是初始值和结束值。
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
currentPoint = (Score) valueAnimator.getAnimatedValue();
invalidate();
}
});
最后ValueAnimator的addUpdateListener来监听并获取动画过渡的值,最后通过invalidate方法不停的重绘。
2、SweepGradient
对于一些3D立体效果的渐变可以尝试用角度渐变来完成一个圆锥形,前两个参数为中心点,然后通过载入的颜色来平均的渐变渲染。
核心代码:
private void startCircleAnimation(TimeInterpolator _interpolator) {
final float core = ((mScore / mCountScore) * 360);
if (animator == null) {
animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Score start = (Score) startValue;
Score end = (Score) endValue;
float startAngle = start.angle;
float endAngle = end.angle;
float startScore = start.score;
float endScore = end.score;
int currentAngle = (int) ((startAngle + (endAngle - startAngle) * fraction) / 2);
int score = (int) ((startScore + (endScore - startAngle) * fraction) / 2);
return new Score(score, currentAngle);
}
}, new Score(0, 0), new Score((int) mScore, (int) core));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
currentPoint = (Score) valueAnimator.getAnimatedValue();
invalidate();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (mOnScoreListener != null) {
mOnScoreListener.finish();
}
}
});
if (_interpolator != null) {
animator.setInterpolator(_interpolator);
}
animator.setDuration(2000);
}
if (!animator.isRunning()) {
animator.start();
}
}
以上就是通过ValueAnimator的ofObject来实现一个过渡动画,在evaluate方法中主要获取分数0-mScore、角度0-core间的过渡值,这里的core是通过当前得分除以总分,最后乘以360得到的一个角度值。接着通过监听函数addUpdateListener方法来获取过渡值并进行重绘。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTextPaint.getTextBounds("" + currentPoint.score * 2, 0, ("" + currentPoint.score * 2).length(), mTextRect);
mMatrix.setRotate(currentPoint.angle * 2, 0, height);
mShader.setLocalMatrix(mMatrix);
rectF.left=mPadding;
rectF.top=mPadding;
rectF.right=width-mPadding;
rectF.bottom=height-mPadding;
canvas.drawArc(rectF, 0, 360, false, mOutCirclePaint);//外圆
canvas.drawCircle(width / 2, height / 2, mInCircleWidth, mInCirclePaint);//内圆
canvas.drawArc(rectF, -(90 + currentPoint.angle), currentPoint.angle * 2, false, mInArcPaint);//圆弧线
canvas.drawText("" + currentPoint.score * 2, width / 2 - mTextRect.width() / 2, height / 2 + mTextRect.height() / 2, mTextPaint);//文字
}
onDraw方法非常简单,依次绘制外圆、内圆、弧线以及文字。