基于material design的过渡加载动画

来源:互联网 发布:梦里花落知多少陆叙 编辑:程序博客网 时间:2024/05/29 05:00

几乎大部分应用都有Spalsh闪屏页面,目的就是进行一些初始化程序,检查版本更新等

那么怎么打造一个炫酷的过渡加载动画呢?google提供了material design的设计风格



分析整个过渡动画,可以分为三个阶段:

1.旋转动画

2.缩放动画

3.水波纹扩散效果(这种效果也是material design风格大量采用的)


1.进入MainActivity

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);final SplashView mSplashView=new SplashView(this);ContentView mContentView=new ContentView(this);FrameLayout fl=new FrameLayout(this);fl.addView(mContentView);fl.addView(mSplashView);setContentView(fl);
mSplashView就是过渡动画的自定义view

mContentView是动画结束之后的主页面,可以使RelativeLayout 也可以是LinearLayout这里简单起见采用ImageView

还有注意的就是,这几个new出来的view默认的布局参数都是matchParent

2.写一个SplashView继承View

定义成员变量和用到的常量

private Paint cPaint;//定义一个画小圆的画笔private Paint bPaint;//画背景的画笔private int width;private int height;private int[] colorArray;//小圆颜色的数组private static final int MAX_DOT_CONUNT=6;//最大小圆的个数private static int BIG_CIRCLE_RADIUS=80;//小圆距离屏幕中心的距离private static final int S_CIRCLE_RADIUS=10;//小圆的半径private float singleAngle;//每个小圆之间的夹角private float rotateAngle=0;//旋转动画转动的角度private int currentRadius=BIG_CIRCLE_RADIUS;//当前小圆距离屏幕中心的距离

初始化变量

public SplashView(Context context) {super(context);init();}//初始化一些变量private void init() {cPaint = new Paint(Paint.ANTI_ALIAS_FLAG);bPaint = new Paint(Paint.ANTI_ALIAS_FLAG);bPaint.setStyle(Paint.Style.STROKE);//设置描边                cPaint.setStyle(Paint.Style.FILL);//设置填充bPaint.setColor(0xffffffff);colorArray = getContext().getResources().getIntArray(R.array.circleColor);singleAngle = (float) (2*Math.PI/MAX_DOT_CONUNT);}


重写onDraw()方法画小圆 onSizeChange()方法拿到view宽高

@Overrideprotected void onDraw(Canvas canvas) {if(design==null){design=new RotateAnim();}design.startAnim(canvas);}

private boolean isCrinkle;private void drawBg(Canvas canvas) {if(!isCrinkle){canvas.drawColor(0xffffffff);}else{//第三阶段动画canvas.drawCircle(width/2, height/2, crinkleCircle+strokeWidth/2, bPaint);}}//计算每个小圆的坐标,画小圆private void drawCircle(Canvas canvas) {for(int i=0;i<colorArray.length;i++){float angle=singleAngle*i+rotateAngle;float cx=(float) (currentRadius*Math.cos(angle))+width/2;float cy=(float) (currentRadius*Math.sin(angle))+height/2;cPaint.setColor(colorArray[i]);canvas.drawCircle(cx, cy, S_CIRCLE_RADIUS, cPaint);}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {width=w;height=h;diagonal = Math.sqrt(w*w+h*h)/2;}

然后就是采用ValueAnimator在不同的阶段进行不同的动画,这里在每个阶段控制不同的动画采用了一个牛逼的设计模式

//定义一个抽象的父类,这种设计模式还是非常叼的,公开课中学到的private abstract class MatrielDesign{public abstract void startAnim(Canvas canvas); }//第一阶段的旋转动画,其实就是改变的rotateAngle大小,让drawCircle()的时候可以每次及时计算出每个小圆的坐标private class RotateAnim extends MatrielDesign{public RotateAnim() {mAnimator=ValueAnimator.ofFloat(0,1.0f);mAnimator.setRepeatCount(Integer.MAX_VALUE);mAnimator.setDuration(1300);mAnimator.setInterpolator(new LinearInterpolator());mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float fraction=(Float) animation.getAnimatedValue();rotateAngle=(float) (Math.PI*fraction);invalidate();}});mAnimator.start();}@Overridepublic void startAnim(Canvas canvas) {drawBg(canvas);drawCircle(canvas);}}//第二阶段的动画,其实就是改变的小圆和屏幕中心的距离//定义ValueAnimator.ofFloat(1.0f,0.2f,1.2f)让动画有一个回弹效果private class ScaleAnim extends MatrielDesign{public ScaleAnim() {mAnimator=ValueAnimator.ofFloat(1.0f,0.2f,1.2f);mAnimator.setDuration(600);mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float fraction=(Float) animation.getAnimatedValue();currentRadius=(int) (BIG_CIRCLE_RADIUS*fraction);invalidate();}});mAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {design=new CrinkleAnim();invalidate();}});mAnimator.start();}@Overridepublic void startAnim(Canvas canvas) {drawBg(canvas);drawCircle(canvas);}}private int crinkleCircle;private double diagonal;private int strokeWidth;//第三阶段水波纹动画,其实就是画背景的时候画一个圆,然后调整画笔的描线宽度就可以了//这个思路也可以做一个圆形头像的效果,你觉得呢?private class CrinkleAnim extends MatrielDesign{public CrinkleAnim(){mAnimator=ValueAnimator.ofFloat(0,1.0f);mAnimator.setDuration(1000);mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float fraction=(Float) animation.getAnimatedValue();crinkleCircle = (int) (diagonal*fraction);strokeWidth=(int) (diagonal-crinkleCircle);bPaint.setStrokeWidth(strokeWidth);invalidate();}});mAnimator.start();}//第三阶段的动画只需要画背景即可@Overridepublic void startAnim(Canvas canvas) {isCrinkle=true;drawBg(canvas);}}
就是这几个关键的内部类,那么他们怎么实现控制动画切换呢

@Overrideprotected void onDraw(Canvas canvas) {if(design==null){design=new RotateAnim();}design.startAnim(canvas);}

onDraw()方法中拿到的是这三个类的父类对象,该对象的具体引用是哪个子类,就进行哪个子类的design.startAnim(canvas);方法,即:对应的动画阶段

最后在第一个旋转阶段是进行闪屏页检查版本更新操作的,那么是不是要在操作完成时提供一个停止动画 进入第二阶段动画的方法呢

private MatrielDesign design;private ValueAnimator mAnimator;//提供给外加调用的,闪屏页检查版本更新后可以调用停止第一阶段动画并且开启第二阶段public void stopRotate(){mAnimator.cancel();design=new ScaleAnim();invalidate();}

下载源码




1 0
原创粉丝点击