上一篇文章已经介绍个属性动画,参考属性动画实战一
今天开始第二个实战动画,仿雅虎加载动画
实现思路:
1、绘制指定个数的圆,并且开启旋转动画
2、内聚动画,绘制的圆向外扩展一定距离后,在开始内聚
3、扩散动画。
实现代码:
创建一个动画抽象类,其他动画类实现该类,完成绘制操作
/**
* 定义一个加载状态动画抽象类
*/
private abstract class LoadStateAnimator {
abstract void draw(Canvas canvas);
}
绘制指定个数圆,并且开启旋转动画
/**
* 旋转动画
*/
private class RotationAnimator extends LoadStateAnimator {
private ValueAnimator mValueAnimator;
//变化的角度
private float changeAngle;
public RotationAnimator() {
fixAngle = (float) (2 * Math.PI / mCircleColors.length);
mValueAnimator = ValueAnimator.ofFloat(0, 2 * (float) Math.PI);
mValueAnimator.setDuration(ANIMATOR_DURATION);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(animation -> {
changeAngle = (float) animation.getAnimatedValue();
invalidate();
});
//无限重复
mValueAnimator.setRepeatCount(-1);
mValueAnimator.start();
}
@Override
void draw(Canvas canvas) {
canvas.drawColor(mBgColor);
//绘制指定个数的圆
for (int i = 0; i < mCircleColors.length; i++) {
mPaint.setColor(ContextCompat.getColor(getContext(), mCircleColors[i]));
float cx = (float) (mCenterX + mCenterRadius * Math.cos(changeAngle + i * fixAngle));
float cy = (float) (mCenterY + mCenterRadius * Math.sin(changeAngle + i * fixAngle));
canvas.drawCircle(cx, cy, mRotationCircleRadius, mPaint);
}
}
public void cancel() {
mValueAnimator.cancel();
}
}
内聚动画,先进行向外扩展,然后向内缩进
/**
* 内聚动画
*/
private class MergeStateAnimator extends LoadStateAnimator {
private ValueAnimator mValueAnimator;
private float changeRadius;
public MergeStateAnimator() {
mValueAnimator = ValueAnimator.ofFloat(mCenterRadius, 0);
mValueAnimator.setDuration(ANIMATOR_DURATION);
//向外扩展一定距离后,向内缩的插值器
mValueAnimator.setInterpolator(new AnticipateInterpolator(3));
mValueAnimator.addUpdateListener(animation -> {
changeRadius = (float) animation.getAnimatedValue();
Log.e("YaHuLoadingView", "changeRadius:" + changeRadius);
invalidate();
});
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLoadStateAnimator = new ExtendStateAnimator();
}
});
mValueAnimator.start();
}
@Override
void draw(Canvas canvas) {
canvas.drawColor(mBgColor);
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i < mCircleColors.length; i++) {
mPaint.setColor(ContextCompat.getColor(getContext(), mCircleColors[i]));
float cx = (float) (mCenterX + changeRadius * Math.cos(i * fixAngle));
float cy = (float) (mCenterY + changeRadius * Math.sin(i * fixAngle));
canvas.drawCircle(cx, cy, mRotationCircleRadius, mPaint);
}
}
}
扩散动画:
/**
* 扩展动画
*/
private class ExtendStateAnimator extends LoadStateAnimator {
private ValueAnimator mValueAnimator;
private float changeCenterRadius;
public ExtendStateAnimator() {
mValueAnimator = ValueAnimator.ofFloat(0, mDiagonal);
mValueAnimator.setDuration(ANIMATOR_DURATION);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(animation -> {
changeCenterRadius = (float) animation.getAnimatedValue();
invalidate();
});
mValueAnimator.start();
}
@Override
void draw(Canvas canvas) {
mPaint.setColor(ContextCompat.getColor(getContext(), mCircleColors[1]));
//画笔的宽度为屏幕对角线一半减去渐变的半径
float strokeWidth = mDiagonal - changeCenterRadius;
mPaint.setStrokeWidth(strokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
//绘制的空心圆的半径等于画笔宽度一半加上渐变半径
float radius = strokeWidth/2 + changeCenterRadius;
canvas.drawCircle(mCenterX, mCenterY, radius, mPaint);
}
}
优化代码:
/**
* 进行代码优化
* @param visibility
*/
@Override
public void setVisibility(int visibility) {
super.setVisibility(INVISIBLE);
//清除动画
clearAnimation();
//移除加载布局
ViewGroup parent = (ViewGroup) getParent();
if (parent !=null){
parent.removeView(this);
}
}
完整代码:
/**
* @author yifuyun
* @date 2020/5/15
*/
public class YaHuLoadingView extends View {
private final long ANIMATOR_DURATION = 5000;
//当前加载的动画状态
private LoadStateAnimator mLoadStateAnimator;
//画笔
private Paint mPaint;
//需要绘制圆颜色总数
private int[] mCircleColors;
//中心点位置
private int mCenterX, mCenterY;
//屏幕对角线的一半
private float mDiagonal;
//中心圆的半径
private int mCenterRadius;
//绘制旋转圆的半径
private int mRotationCircleRadius;
//绘制View背景颜色
private int mBgColor = Color.WHITE;
//初始角度
private float fixAngle;
//是否所有动画都已经结束
private boolean mIsAllAnimatorFinish = true;
//实时记录当前旋转圆的角度
private double[] mRotationCircleCurrentAngles;
public YaHuLoadingView(Context context) {
this(context, null);
}
public YaHuLoadingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public YaHuLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initParams();
}
private void initParams() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mCircleColors = new int[]{R.color.orange, R.color.aqua, R.color.yellow,
R.color.blue, R.color.green, R.color.pink};
mRotationCircleCurrentAngles = new double[mCircleColors.length];
mCenterX = getMeasuredWidth() / 2;
mCenterY = getMeasuredHeight() / 2;
mDiagonal = (float) Math.sqrt(mCenterX * mCenterX + mCenterY * mCenterY);
mCenterRadius = mCenterX / 3;
mRotationCircleRadius = mCenterRadius / 8;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mLoadStateAnimator == null) {
mLoadStateAnimator = new RotationAnimator();
}
mLoadStateAnimator.draw(canvas);
}
/**
* 开启内聚动画
*/
public void startMergeAnimator() {
if (!mIsAllAnimatorFinish) {
return;
}
mIsAllAnimatorFinish = false;
if (mLoadStateAnimator instanceof RotationAnimator) {
RotationAnimator animator = (RotationAnimator) mLoadStateAnimator;
animator.cancel();
}
mLoadStateAnimator = new MergeStateAnimator();
}
/**
* 定义一个加载状态动画抽象类
*/
private abstract class LoadStateAnimator {
abstract void draw(Canvas canvas);
abstract void cancel();
}
/**
* 旋转动画
*/
private class RotationAnimator extends LoadStateAnimator {
private ValueAnimator mValueAnimator;
//变化的角度
private float changeAngle;
public RotationAnimator() {
fixAngle = (float) (2 * Math.PI / mCircleColors.length);
mValueAnimator = ValueAnimator.ofFloat(0, 2 * (float) Math.PI);
mValueAnimator.setDuration(ANIMATOR_DURATION);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(animation -> {
changeAngle = (float) animation.getAnimatedValue();
invalidate();
});
//无限重复
mValueAnimator.setRepeatCount(-1);
mValueAnimator.start();
}
@Override
void draw(Canvas canvas) {
canvas.drawColor(mBgColor);
for (int i = 0; i < mCircleColors.length; i++) {
mPaint.setColor(ContextCompat.getColor(getContext(), mCircleColors[i]));
mRotationCircleCurrentAngles[i]= changeAngle + i * fixAngle;
float cx = (float) (mCenterX + mCenterRadius * Math.cos(mRotationCircleCurrentAngles[i]));
float cy = (float) (mCenterY + mCenterRadius * Math.sin(mRotationCircleCurrentAngles[i]));
canvas.drawCircle(cx, cy, mRotationCircleRadius, mPaint);
}
}
@Override
public void cancel() {
mValueAnimator.cancel();
mValueAnimator.removeAllUpdateListeners();
}
}
/**
* 内聚动画
*/
private class MergeStateAnimator extends LoadStateAnimator {
private ValueAnimator mValueAnimator;
private float changeRadius;
public MergeStateAnimator() {
mValueAnimator = ValueAnimator.ofFloat(mCenterRadius, 0);
mValueAnimator.setDuration(ANIMATOR_DURATION);
mValueAnimator.setInterpolator(new AnticipateInterpolator(3));
mValueAnimator.addUpdateListener(animation -> {
changeRadius = (float) animation.getAnimatedValue();
Log.e("YaHuLoadingView", "changeRadius:" + changeRadius);
invalidate();
});
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
cancel();
mLoadStateAnimator = new ExtendStateAnimator();
}
});
mValueAnimator.start();
}
@Override
void draw(Canvas canvas) {
canvas.drawColor(mBgColor);
mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i < mCircleColors.length; i++) {
mPaint.setColor(ContextCompat.getColor(getContext(), mCircleColors[i]));
float cx = (float) (mCenterX + changeRadius * Math.cos(mRotationCircleCurrentAngles[i]));
float cy = (float) (mCenterY + changeRadius * Math.sin(mRotationCircleCurrentAngles[i]));
canvas.drawCircle(cx, cy, mRotationCircleRadius, mPaint);
}
}
@Override
public void cancel() {
mValueAnimator.cancel();
mValueAnimator.removeAllUpdateListeners();
mValueAnimator.removeAllListeners();
}
}
/**
* 扩展动画
*/
private class ExtendStateAnimator extends LoadStateAnimator {
private ValueAnimator mValueAnimator;
private float changeCenterRadius;
public ExtendStateAnimator() {
mValueAnimator = ValueAnimator.ofFloat(0, mDiagonal);
mValueAnimator.setDuration(ANIMATOR_DURATION);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(animation -> {
changeCenterRadius = (float) animation.getAnimatedValue();
invalidate();
});
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mIsAllAnimatorFinish = true;
}
});
mValueAnimator.start();
}
@Override
void draw(Canvas canvas) {
mPaint.setColor(ContextCompat.getColor(getContext(), mCircleColors[1]));
float strokeWidth = mDiagonal - changeCenterRadius;
mPaint.setStrokeWidth(strokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
float radius = strokeWidth / 2 + changeCenterRadius;
canvas.drawCircle(mCenterX, mCenterY, radius, mPaint);
}
@Override
public void cancel() {
mValueAnimator.cancel();
mValueAnimator.removeAllUpdateListeners();
mValueAnimator.removeAllListeners();
}
}
/**
* 进行代码优化
*
* @param visibility
*/
@Override
public void setVisibility(int visibility) {
super.setVisibility(INVISIBLE);
//清除动画
clearAnimation();
//移除加载布局
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
//取消动画
if (mLoadStateAnimator != null) {
mLoadStateAnimator.cancel();
mLoadStateAnimator = null;
}
}
}