实现效果:
这边其实是两个View,加载动画覆盖在需要进入的页面的上面
动画分为三种状态
1:一开始的小圆球旋转动画
2:小圆逃逸后再聚合动画
3:水波纹扩散动画
一、初始化基本参数
private void init(Context context) {
//这边mCircleColors为六个小球的颜色
mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
//画笔初始化
//消除锯齿
mPaint.setAntiAlias(true);
mPaintBackground.setAntiAlias(true);//该Paint为加载动画白色背景墙
//设置样式---边框样式--描边
mPaintBackground.setStyle(Paint.Style.STROKE);
mPaintBackground.setColor(mSplashBgColor);
}
二、使用策略模式分别实现三种状态
private SplashState mState=null;
private abstract class SplashState{
public abstract void drawSate(Canvas canvas);
}
先定义好是要实现的策略类
1)旋转动画
/**
* 1.旋转动画
* 控制各个小圆的坐标----控制小圆的角度变化------属性动画ValueAnimator
*/
private class RotateState extends SplashState{
public RotateState() {
//1.动画的初始工作, 0~2π 圆周角的弧度数
mAnimator=ValueAnimator.ofFloat(0f,(float)(Math.PI*2));
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//计算某个时刻当前的角度是多少
mCurrentRotationAngle= (float) animation.getAnimatedValue();
invalidate();
}
});
// 2.开启动画
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setDuration(mRotationDuration);
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
public void cancel(){
mAnimator.cancel();
}
@Override
public void drawSate(Canvas canvas) {
//1.背景---擦黑板,涂成白色
drawBackground(canvas);
//2.绘制小圆
drawCircles(canvas);
}
}
构造方法中初始化好旋转动画所需的属性动画
绘制小圆的方法
private void drawCircles(Canvas canvas) {
//每个小圆之间的间隔角度2π/小圆的个数
float rotationAngel= (float) (2*Math.PI/mCircleColors.length);
for (int i=0;i<mCircleColors.length;i++){
/*
* x=r*cos(a)+centerX
* y=r*sin(a)+centerY
*/
double angle=i*rotationAngel+mCurrentRotationAngle;
//每个小圆i*间隔角度+旋转角度=当前小圆的真实角度
float cx= (float) (mCurrentRotationRadius*Math.cos(angle)+mCenterX);
float cy= (float) (mCurrentRotationRadius*Math.sin(angle)+mCenterY);
mPaint.setColor(mCircleColors[i]);
canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
}
}
1.首先先求出一个由六个小圆组成大圆之间它们之间的间隔角度为2π/小球个数
2.再根据属性动画mCurrentRotationAngle计算的旋转角度以及间隔角度算出当前小球的真实角度
3.mCenterX和mCenterY取的是屏幕宽高的一半,mCurrentRotationRadius为定义的大圆的半径
4.这样就算出了小圆的圆心坐标
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mState==null){
//开启第一个动画,旋转动画
mState=new RotateState();
}
//调用绘制方法
mState.drawSate(canvas);
}
在onDraw开启第一个动画,并调用绘制方法开始无限循环的旋转小球,在主界面顶一个延迟来结束第一个动画进入第二个动画
private void startLoadData() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//数据加载完毕,进入主界面---》开启后面的两个动画
mSplashView.splashDisapper();
}
},3000);
}
自定义View中:
public void splashDisapper(){
//开启后面两个动画
//换模板if--换状态
if(mState!=null&&mState instanceof RotateState){
//结束旋转动画
RotateState rotateState= (RotateState) mState;
rotateState.cancel();
post(new Runnable() {
@Override
public void run() {
mState=new MergingState();
}
});
}
}
2)聚合动画
/**
* 2.聚合动画
* 要素:大圆的半径不断地变大---》变小----》小圆的坐标
*/
private class MergingState extends SplashState{
public MergingState() {
//r~0的某个值
mAnimator=ValueAnimator.ofFloat(mRotationRadius,0);
mAnimator.setDuration(mRotationDuration);
mAnimator.setInterpolator(new OvershootInterpolator(10f));
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//某个时刻当前的大圆半径是多少?
mCurrentRotationRadius= (float) animation.getAnimatedValue();
invalidate();
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mState=new ExpandState();
}
});
mAnimator.start();
}
@Override
public void drawSate(Canvas canvas) {
//1.背景---擦黑板,涂成白色
drawBackground(canvas);
//2.绘制小圆
drawCircles(canvas);
}
}
1.mRotationRadius大圆初始半径到0的值,加入插值器OvershootInterpolator(向前甩一定值后再回到原来位置)
2.当持续时间完成调用下一个动画,这边drawState中同样是需要画白色背景墙和小圆的,只是这个时候mCurrentRotationRadius大圆半径进行了变化
3)水波纹扩散动画
/**
* 3.水波纹扩散动画
* 画一个空心圆,让它的画笔的粗细变成很大---不断地减少画笔的粗细
* 空心圆变化的范围:0~对角线/2
*/
private class ExpandState extends SplashState{
public ExpandState() {
//1200ms,计算某个时刻当前的大圆半径是多少?r~对角线一半的某个值
mAnimator=ValueAnimator.ofFloat(mCircleRadius,mDiagonalDist);
mAnimator.setDuration(mRotationDuration);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mHoleRadius= (float) animation.getAnimatedValue();
invalidate();
}
});
mAnimator.start();
}
@Override
public void drawSate(Canvas canvas) {
//1.背景---擦黑板,涂成白色
drawBackground(canvas);
}
}
1.第三个动画实现方式根据画一个空心圆,控制其stroWidth大小,不断减小实现扩散
2.mHoleRadius为空心圆的初始半径,这个时候处在第三状态的一开始所有的小球是已经聚集在一起的,所以初始半径这边根据属性动画mCircleRadius(小圆半径)到mDiagonalDist
3.mDiagonalDist:为屏幕对角线的一半,至于为什么是这个需要看一下drawBackground方法
private void drawBackground(Canvas canvas) {
if(mHoleRadius>0f){//第三个动画开始执行
//得到画笔的宽度---对角线的一半-空心圆的半径
float stroWidth=mDiagonalDist-mHoleRadius;
mPaintBackground.setStrokeWidth(stroWidth);
//画圆的半径=空心圆的半径+画笔的宽度/2
float radius=mHoleRadius+stroWidth/2;
canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);
}else{//其他两个动画
canvas.drawColor(mSplashBgColor);
}
}
这样空心圆半径不断变大,stroWdith不断减少,圆半径随之改变,就实现了扩散的方法