整体动画分为三个阶段,分别为小球旋转、聚拢和显示内容。通过状态模式控制视图的动画绘制,不同阶段代表视图的不同状态,
private abstract static class SplashState {
public abstract void drawSplash(Canvas canvas);
}
旋转
绘制小球为第一阶段,继承SplashState,重写drawSplash方法。首先根据颜色数量计算出每个小球之间的角度,通过三角函数计算出每个小球的具体位置。
canvas.drawColor(Color.WHITE);
float rotationAngle = (float) (Math.PI * 2 / colorArr.length);
for (int i = 0; i < colorArr.length; i++) {
mPaint.setColor(colorArr[i]);
double angle = i * rotationAngle + mCurValue;
float x = (float) (Math.cos(angle) * cR);
float y = (float) (Math.sin(angle) * cR);
canvas.drawCircle(cX + x, cY + y, colorRadius, mPaint);
}
在构造函数中创建属性动画,控制小球的旋转,设置线性插值器控制均速旋转。
public RotateState(View view) {
valueAnimator = ValueAnimator.ofFloat(0f, (float) (Math.PI * 2));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setDuration(1000);
valueAnimator.setRepeatCount(1);
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
valueAnimator = null;
mState = new ScaleState(view);
postInvalidate();
}
});
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
聚拢
小球聚拢为第二阶段,继承SplashState,重写drawSplash方法。首先根据颜色数量计算出每个小球之间的角度,通过三角函数计算出每个小球的具体位置,具体位置为初始位置加上聚拢进度。
@Override
public void drawSplash(Canvas canvas) {
canvas.drawColor(Color.WHITE);
float rotationAngle = (float) (Math.PI * 2 / colorArr.length);
for (int i = 0; i < colorArr.length; i++) {
mPaint.setColor(colorArr[i]);
double angle = i * rotationAngle;
float x = (float) (Math.cos(angle) * mCurValue);
float y = (float) (Math.sin(angle) * mCurValue);
canvas.drawCircle(cX + x, cY + y, colorRadius, mPaint);
}
}
属性动画自带类似弹性插值器,通过反向进行实现聚拢效果。
public ScaleState(View view) {
valueAnimator = ValueAnimator.ofFloat(15, mScaleMax);
valueAnimator.setInterpolator(new OvershootInterpolator(10f));
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
valueAnimator = null;
mState = new FinalState(view);
postInvalidate();
}
});
valueAnimator.reverse();
}
显示
显示内容为第三阶段,继承SplashState,重写drawSplash方法,需要根据进度绘制空心圆的内容,空心圆内容为background。
@Override
public void drawSplash(Canvas canvas) {
//得到画笔的宽度 = 对角线/2 - 空心圆的半径
float strokeWidth = mDiagonalDist - mCurValue;
mPaintBackground.setStrokeWidth(strokeWidth);
//画圆的半径 = 空心圆的半径 + 画笔的宽度/2
float radius = mCurValue + strokeWidth / 2;
canvas.drawCircle(cX, cY, radius, mPaintBackground);
}
public FinalState(View view) {
int width = view.getWidth();
int height = view.getHeight();
mDiagonalDist = (float) Math.sqrt(width * width + height * height);
mCurValue = 0;
valueAnimator = ValueAnimator.ofFloat(30, mDiagonalDist);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
valueAnimator = null;
}
});
valueAnimator.start();
}
其他
自定义属性获取
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SplashView);
// 用于获取数组资源
int resourceId = typedArray.getResourceId(R.styleable.SplashView_colorArr, 0);
if (resourceId != 0) {
final TypedArray resourceArray = getResources().obtainTypedArray(resourceId);
colorArr = new int[resourceArray.length()];
for (int i = 0; i < resourceArray.length(); i++) {
colorArr[i] = resourceArray.getColor(i, 0);
}
resourceArray.recycle();
}
typedArray.recycle();
相关画笔
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
cR = 150;
colorRadius = 30;
mScaleMax = 170;
mState = new RotateState(this);
mPaintBackground = new Paint();
mPaintBackground.setStyle(Paint.Style.STROKE);
mPaintBackground.setColor(Color.WHITE);
}
控件onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mState != null) {
mState.drawSplash(canvas);
}
}