首先要感谢Ricky大神的分享,对于那些想入门自定义view的同学来说,这个例子还是不错的,先看一张效果图:
效果是不是很炫呢,下面咱们就解析下这个动画,这个动画一共分为四部分:
1、小圆旋转:不断改变小圆位置,形成旋转的效果;
2、小圆向中心聚合:不断减小小圆离中心点的距离,形成向中心聚合的效果;
3、圆缩放:不断改变绘制圆的半径,形成缩放的效果;
4、扩散:将画笔的宽度设置的非常宽,绘制空心圆,不断增加空心圆的半径,同时减小画笔的宽度,形成扩散的动画;
大家都知道,绘制view是在onDraw(Canvas canvas)方法中执行的,如果将所有的绘制和逻辑都写在一个方法中,那代码肯定是相当的臃肿,可读性也会变的特别的差,然而不得不说JAVA的多态真是一个很棒的设计,这里设计一个接口,用四个类去实现这个接口,每个类对应一个动画,动态改变变量的指向,以达到绘制不同动画的目的:
</pre><p><pre name="code" class="html">/**
* 画不同图形的接口
*/
public interface LoadState {
/**
* 画不同图形的接口
*/
public abstract void drawState(Canvas canvas);
}
接口的四个实现类:
1、RotationState:旋转动画;
2、MergingState:聚合动画;
3、CircleStae:圆缩放动画;
4、ExpandingStae:扩散动画;
这样的话,我们只需在 onDraw(Canvas canvas)中简单的写这几行代码就搞定了:
@Override
protected void onDraw(Canvas canvas) {
//如果开始为空,则执行旋转动画
if(mState == null ){
mState = new RotationState();
}
//这边采用多态的方式,不断改变mState对象类型,执行不同的绘图动作
mState.drawState(canvas);
super.onDraw(canvas);
}
具体代码如下:
package com.longshao.loadanimademo;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
/**
* 加载动画view
* 动画共分为四部分:
* 1、无限循环旋转(加载中)
* 2、六个圆聚合在一起(加载完成执行的动画)
* 3、圆缩放
* 4、扩散动画,显示下面view
* Created by Administrator on 2015/8/5.
*/
public class LoadView extends View{
/**
* 大圆的半径(里面含有好多小圆)
*/
private float mRotationRadius = 90;
/**
* 小圆的半径
*/
private float mCircleRadius = 18;
/**
* 小圆的颜色列表
*/
private int[] mCircleColors ;
/**
*小圆旋转一周需要的时间
*/
private long mRotationDuration = 1200;
/**
*其他动画执行的时间(除了旋转的动画)
*/
private long mSplashDuration = 500;
/**
* view的背景颜色
*/
private int mBgColor = Color.WHITE;
/**
* 当前大圆的半径(动态变化)
*/
private float mCurrentRatationRadios;
/**
* 当前大圆旋转的角度(弧度)
*/
private float mCurrentRotationAngle = 0f;
/**
* 空心圆半径
*/
private float mHoleRadius =0f;
/**
* 绘制圆的画笔
*/
private Paint mPaint = new Paint();
/**
* 绘制背景的画笔
*/
private Paint mBgPaint = new Paint();
/**
*view中心的坐标
*/
private float mCenterX;
private float mCenterY;
/**
* view对角线的一半
*/
private float mDiagonalDist;
/**
* 保存当前动画状态-->当前在执行那种动画
*/
private LoadState mState = null;
/**
*小圆之间的间隔角度
*/
private float mRotationAngle = 0f;
/**
* 聚合后缩放圆的半径
*/
private float mScaleCircle ;
public LoadView(Context context) {
this(context, null);
}
public LoadView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化
*/
private void init(Context context) {
//抗锯齿
mPaint.setAntiAlias(true);
mBgPaint.setAntiAlias(true);
//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
mPaint.setDither(true);
mBgPaint.setDither(true);
//设置画笔的样式:空心
mBgPaint.setStyle(Paint.Style.STROKE);
//设置画笔颜色
mBgPaint.setColor(mBgColor);
//旋转的时候大圆的半径不变,为初始化值
mCurrentRatationRadios = mRotationRadius;
//聚合后放大圆的初始半径为小圆半径
mScaleCircle = mCircleRadius;
//获取颜色数组,小圆的个数和定义颜色的个数相等
mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
if(mCircleColors.length>0){
//每个小圆之间的间隔角度(弧度)
mRotationAngle = (float) (2*Math.PI/mCircleColors.length);
}
}
/**
*获取view的中点坐标,view对角线长度的一半
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w/2f;
mCenterY = h/2f;
mDiagonalDist = (float) (Math.sqrt(w*w + h*h)/2f);
}
@Override
protected void onDraw(Canvas canvas) {
//如果开始为空,则执行旋转动画
if(mState == null ){
mState = new RotationState();
}
//这边采用多态的方式,不断改变mState对象类型,执行不同的绘图动作
mState.drawState(canvas);
super.onDraw(canvas);
}
/**
* 数据加载完毕,关闭第一个动画,执行后面三个动画
*/
public void splashAndDisappear(){
//取消第一个动画
if(mState != null && mState instanceof RotationState){
((RotationState) mState).cancel();
post(new Runnable() {
@Override
public void run() {
mState = new MergingState();
}
});
}
}
/**
* 旋转动画
*/
private class RotationState implements LoadState{
private ValueAnimator animator;
public RotationState() {
animator = ValueAnimator.ofFloat(0f,(float)(2*Math.PI));
animator.setDuration(mRotationDuration);
//设置无限循环
animator.setRepeatCount(ValueAnimator.INFINITE);
//匀速旋转
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取大圆旋转的当前角度
mCurrentRotationAngle = (float) animation.getAnimatedValue();
//重绘图像
invalidate();
}
});
animator.start();
}
@Override
public void drawState(Canvas canvas) {
clearCanvas(canvas);
darwCircle(canvas);
}
/**
* 取消旋转动画
*/
public void cancel(){
animator.cancel();
}
}
/**
* 绘制小圆
* @param canvas
*/
private void darwCircle(Canvas canvas) {
for (int i = 0; i < mCircleColors.length; i++){
//设置画笔颜色
mPaint.setColor(mCircleColors[i]);
//小圆的x坐标
float cx = (float) (mCurrentRatationRadios*Math.cos(mCurrentRotationAngle + mRotationAngle * i) + mCenterX);
float cy = (float) (mCurrentRatationRadios*Math.sin(mCurrentRotationAngle+mRotationAngle*i) + mCenterY);
canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
}
}
/**
* 清空画布
* @param canvas
*/
private void clearCanvas(Canvas canvas) {
//如果空心圆的半径为0,则清空画布,如果mHoleRadius不为零,说明正在执行扩散动画
if(mHoleRadius>0f){
//画笔的宽度
mBgPaint.setStrokeWidth(mDiagonalDist - mHoleRadius);
//空心圆的半径
float radius = mDiagonalDist/2 + mHoleRadius/2;
canvas.drawCircle(mCenterX,mCenterY,radius,mBgPaint);
}else{
canvas.drawColor(mBgColor);
}
}
/**
* 聚合动画
*/
private class MergingState implements LoadState{
private ValueAnimator animator;
public MergingState() {
animator = ValueAnimator.ofFloat(0f,mRotationRadius);
//开始有个弹射效果,输入的参数越大,弹射效果越明显
animator.setInterpolator(new OvershootInterpolator(6f));
animator.setDuration(mSplashDuration);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentRatationRadios = (float) animation.getAnimatedValue();
invalidate();
}
});
//反向计算
animator.reverse();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
post(new Runnable() {
@Override
public void run() {
mState = new CircleStae();
}
});
}
});
}
@Override
public void drawState(Canvas canvas) {
clearCanvas(canvas);
darwCircle(canvas);
}
}
/**
* 圆缩放动画
*/
private class CircleStae implements LoadState{
private ValueAnimator animator;
public CircleStae() {
animator = ValueAnimator.ofFloat(mCircleRadius,2.5f*mCircleRadius,mCircleRadius);
animator.setDuration(mSplashDuration);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//当前圆的圆心
mScaleCircle = (float) animation.getAnimatedValue();
//重绘图像
invalidate();
}
});
animator.start();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
post(new Runnable() {
@Override
public void run() {
mState = new ExpandingStae();
}
});
}
});
}
@Override
public void drawState(Canvas canvas) {
clearCanvas(canvas);
canvas.drawCircle(mCenterX, mCenterY, mScaleCircle,mPaint);
}
}
/**
* 扩散动画
*/
private class ExpandingStae implements LoadState{
private ValueAnimator animator;
public ExpandingStae() {
animator = ValueAnimator.ofFloat(0,mDiagonalDist);
animator.setDuration(mSplashDuration);
//扩散我这边使用的是匀速,可以换成加速:AccelerateInterpolator
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取空心圆的半径
mHoleRadius = (float) animation.getAnimatedValue();
//重绘图像
invalidate();
}
});
animator.start();
}
@Override
public void drawState(Canvas canvas) {
clearCanvas(canvas);
}
}
}
点击资源下载