在《Android属性动画1-----属性动画的简单使用》中,简单介绍了属性动画的基本使用,在本章节中,将会结合前面的基础知识,实现一个自定义属性动画。

基本的特效:旋转 + 逃逸聚合 + 扩散 + 策略设计模式,完整源码如下:

public class SplashView extends View {    //动画    private ValueAnimator valueAnimator;    //绘制圆的画笔    private Paint mPaint = new Paint();    //绘制背景的画笔    private Paint mPaintBackground = new Paint();    //当前大圆旋转角度(弧度)    private float mCurrentRotationAngle = 0F;    // 大圆(里面包含很多小圆的)的半径    private float mRotationRadius = 90;    // 每一个小圆的半径    private float mCircleRadius = 18;    //当前大圆的半径    private float mCurrentRotationRadius = mRotationRadius;    //颜色的集合    private int[] mCircleColors;    //圆圈旋转的时间    private int mRotationDuration = 1200;    // 屏幕正中心点坐标    private float mCenterX;    private float mCenterY;    //屏幕对角线的一般    private int DisBank;    //空心圆初始半径    private float mHoleRadius = 0F;    public SplashView(Context context) {        this(context,null,0);    }    public SplashView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs,0);    }    public SplashView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        //抗锯齿        mPaint.setAntiAlias(true);        mPaintBackground.setAntiAlias(true);        //描边        mPaintBackground.setStyle(Paint.Style.STROKE);        mPaintBackground.setColor(Color.DKGRAY);        //颜色集合        mCircleColors = new int[]{R.color.colorBlue,R.color.colorOrange,R.color.colorGreen,                R.color.colorRed,R.color.colorYellow,R.color.colorPupple};    }    //初始状态    SplashState state = null;    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if(state == null){            //初始状态 旋转            state = new RotateState();        }        //画圆        state.drawState(canvas);    }    //状态转变    public void splashDisappear(){        if(state != null && state instanceof RotateState){            //动画取消            state.cancel();            post(new Runnable() {                @Override                public void run() {                    //开始聚合动画                    state = new MergingState();                }            });        }    }    //业务逻辑  状态    //1.刚进来时,执行旋转动画    private class RotateState extends SplashState{        public RotateState(){            //2π == 360度            valueAnimator = ValueAnimator.ofFloat(0, (float) (Math.PI*2));            //设置匀速插值器            valueAnimator.setInterpolator(new LinearInterpolator());            //设置监听            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @Override                public void onAnimationUpdate(ValueAnimator animation) {                    //当前圆的角度                    mCurrentRotationAngle = (float) animation.getAnimatedValue();                    //重新绘制                    postInvalidate();                }            });            valueAnimator.setDuration(mRotationDuration);            valueAnimator.setRepeatCount(ValueAnimator.INFINITE);            valueAnimator.start();        }        @Override        public void drawState(Canvas canvas) {            //画背景            drawBackground(canvas);            //画圆            drawCircle(canvas);        }    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mCenterX = w/2;        mCenterY = h/2;        //屏幕对角线的一半        DisBank = (int) (Math.sqrt(w*w + h*h)/2f);    }    private void drawCircle(Canvas canvas) {        //每个圆对应的角度(360/6 = 60°)        float rotationAngle = (float) (Math.PI * 2 / mCircleColors.length);        //关于每个点的坐标        for (int i = 0; i < mCircleColors.length; i++) {            //角度是动态变化的            float angle = i*rotationAngle + mCurrentRotationAngle;            float cx = (float) (mCenterX + mCurrentRotationRadius * Math.cos(angle));            float cy = (float) (mCenterY + mCurrentRotationRadius * Math.sin(angle));            //开始画            mPaint.setColor(mCircleColors[i]);            canvas.drawCircle(cx,cy,mCircleRadius,mPaint);        }    }    private void drawBackground(Canvas canvas) {        if(mHoleRadius >0){            //画圆            float strokWidth = DisBank - mHoleRadius;            mPaintBackground.setStrokeWidth(strokWidth);            float radius = mHoleRadius + strokWidth/2;            canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);        }else {            canvas.drawColor(Color.WHITE);        }    }    //2.数据加载完毕之后,聚合逃逸动画    private class MergingState extends SplashState{        public MergingState(){            valueAnimator = ValueAnimator.ofFloat(mRotationRadius,0);            valueAnimator.setDuration(mRotationDuration);            valueAnimator.setInterpolator(new OvershootInterpolator(10));            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @Override                public void onAnimationUpdate(ValueAnimator animation) {                    mCurrentRotationRadius = (float) animation.getAnimatedValue();                    invalidate();                }            });            //监听动画完成            valueAnimator.addListener(new AnimatorListenerAdapter() {                @Override                public void onAnimationEnd(Animator animation) {                    super.onAnimationEnd(animation);                    //启动扩散动画                    state = new ExpandState();                }            });            valueAnimator.start();        }        @Override        public void drawState(Canvas canvas) {            drawBackground(canvas);            drawCircle(canvas);        }    }    //3.扩散动画  以屏幕到中心线的对角线为半径    private class ExpandState extends SplashState{        public  ExpandState(){            valueAnimator = ValueAnimator.ofFloat(mCircleRadius,DisBank);            valueAnimator.setDuration(mRotationDuration);//            valueAnimator.setInterpolator(new AccelerateInterpolator(5));            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @Override                public void onAnimationUpdate(ValueAnimator animation) {                    mHoleRadius = (float) animation.getAnimatedValue();                    invalidate();                }            });            valueAnimator.start();        }        @Override        public void drawState(Canvas canvas) {            drawBackground(canvas);        }    }    //策略模式    private abstract class SplashState{        //画动画状态        public abstract void drawState(Canvas canvas);        //取消动画        public void cancel(){            valueAnimator.cancel();        }    }}

下面就详细介绍动画的实现。

1、旋转动画

该动画主要分为3种:一开始是旋转动画,然后是聚合逃逸动画,最后是一个扩散动画,3种动画为3种状态,因此使用策略模式,实现3种状态。

策略模式的优点:
(1)如果在使用上述3种状态时,在判断某种状态时往往会使用if-else语句,也就是用户不选择A那么就选择B这样的一种情况。这种情况耦合性太高了,而且代码臃肿,有了策略模式我们就可以避免这种现象,
(2)策略模式遵循开闭原则,实现代码的解耦合。扩展新的方法时也比较方便,只需要继承策略基类就好了。

 private abstract class SplashState{        //画动画        public abstract void drawState(Canvas canvas);        //取消动画        public void cancel(){            animator.cancel();        }    }        //3种状态的实现    //旋转动画    public class RotateState extends SplashState{        @Override        public void drawState(Canvas canvas) {                    }    }    //聚合逃逸动画    public class MergingState extends SplashState{        @Override        public void drawState(Canvas canvas) {        }    }    //扩散动画    public class ExpandState extends SplashState{        @Override        public void drawState(Canvas canvas) {        }    }

完成了3种状态的实现之后,默认打开的第一个动画就是旋转动画,因此在onDraw方法中,就需要先画出旋转动画。

SplashState mState = null;    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if(mState == null){            mState = new RotateState();        }        //开始画动画        mState.drawState(canvas);    }

接下来就需要处理RotateState旋转类动画中的逻辑。

//旋转动画    public class RotateState extends SplashState{        public RotateState(){            //360度旋转            animator = ValueAnimator.ofFloat(0, (float) (Math.PI * 2));            //匀速旋转            animator.setInterpolator(new LinearInterpolator());            //设置监听            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @Override                public void onAnimationUpdate(ValueAnimator animation) {                    //获取当前大圆旋转的角度                    mCurrentRotationAngle = (float) animation.getAnimatedValue();                    postInvalidate();                }            });            //设置动画的时间            animator.setDuration(mRotationDuration);            //开启动画            animator.start();        }        @Override        public void drawState(Canvas canvas) {            //画背景            drawBackground(canvas);            //画圆圈            drawCircle(canvas);        }    }    private void drawCircle(Canvas canvas) {        //360度,每个小圆平均分得的度数        float mCircleAngle = (float) (Math.PI * 2 / mCircleColors.length);        for (int i = 0; i < mCircleColors.length; i++) {            //当前每个圆对应的角度            float mCurrentCircleAngle = i * mCircleAngle + mCurrentRotationAngle;            float cx = (float) (mCenterX + mRotationRadius * Math.cos(mCurrentCircleAngle));            float cy = (float) (mCenterY + mRotationRadius * Math.sin(mCurrentCircleAngle));            //开始画圆            mPaint.setColor(mCircleColors[i]);            canvas.drawCircle(cx,cy,mCircleRadius,mPaint);        }    }


在旋转动画完成之后,需要切换状态,转变为聚合逃逸动画

public void splashDisappear(){        if(mState != null && mState instanceof RotateState){            //结束当前的动画            mState.cancel();                        post(new Runnable() {                @Override                public void run() {                    mState = new MergingState();                }            });        }    }

2、聚合逃逸动画

聚合逃逸动画,在旋转动画完成之后,会以中心为基准,全部的圆向圆心内聚集,因此在处理这一部分逻辑时,需要动态改变大圆的半径即可。

//聚合逃逸动画    public class MergingState extends SplashState{        public MergingState(){            //从大圆的半径---递减到0            animator = ValueAnimator.ofFloat(mRotationRadius,0);            //添加张力插值器            animator.setInterpolator(new OvershootInterpolator(10));            //添加监听            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @Override                public void onAnimationUpdate(ValueAnimator animation) {                    mCurrentCircleRadius = (float) animation.getAnimatedValue();                    invalidate();                }            });            animator.setDuration(mRotationDuration);            animator.start();        }        @Override        public void drawState(Canvas canvas) {            drawBackground(canvas);            drawCircle(canvas);        }    }

3、扩散动画

聚合动画的执行需要监听是否完成,如果已经完成了,那么就开始扩散动画。

//监听动画是否完成            animator.addListener(new AnimatorListenerAdapter() {                @Override                public void onAnimationEnd(Animator animation) {                    super.onAnimationEnd(animation);                    //启动扩散动画                    mState = new ExpandState();                }            });

当监听到聚合动画已经完成,就开启扩散动画,接下来需要处理扩散动画的逻辑。
扩散动画,是以屏幕对角线的一半为半径,然后从小圆半径开始,逐渐扩散。

 //扩散动画    public class ExpandState extends SplashState{        public ExpandState(){            //设置动画 最小半径---屏幕对角线的一半            animator = ValueAnimator.ofFloat(mCircleRadius,mDiaLine);            //设置监听            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                @Override                public void onAnimationUpdate(ValueAnimator animation) {                    mHoleRedis = (float) animation.getAnimatedValue();                    invalidate();                }            });            animator.setDuration(mRotationDuration);            //开启动画            animator.start();        }        @Override        public void drawState(Canvas canvas) {            drawBackground(canvas);        }    }
private void drawBackground(Canvas canvas) {        if(mHoleRedis >0) {            //画圆            float strokeWidth = mDiaLine - mCircleRadius;            mBackgroundPaint.setStrokeWidth(strokeWidth);            //空心圆的半径            float redis = mHoleRedis + strokeWidth/2;            canvas.drawCircle(mCenterX,mCenterY,redis,mBackgroundPaint);        }else {            canvas.drawColor(getResources().getColor(R.color.colorBlue));        }    }

通过以上3步骤,配合ValueAnimator实现了整体的动画设计,在实际的项目中,大部分的动画实现都是以上套路,只是其中的细节可能涉及估值器和插值器的使用,下一节中,将会手写一个动画加载框架。

更多相关文章

  1. Android属性动画---Property Animation(六)
  2. Android(安卓)时间选择器(TimeBucketSelector)
  3. [sg] Android(安卓)4.4 屏幕旋转的两种方式
  4. android opengles 实现翻牌效果
  5. android studio 绘制时钟刻度表盘的虚拟动画。
  6. Material Design动画
  7. android 关于Button在StateListDrawable 中使用AnimationDrawabl
  8. 显示gif动画(帧动画的播放)
  9. Android动画总结 (valueAnimator、objectAnimator、AnimatorSet、

随机推荐

  1. 从Android到iOS开发——(1)、objective-c
  2. 抛砖引玉,谈谈Android移植到现有硬件平台
  3. mysql横向转纵向、纵向转横向排列的方法
  4. navicat创建MySql定时任务的方法详解
  5. MySql比较运算符正则式匹配REGEXP的详细
  6. 记一次MySQL的优化案例
  7. 谈谈MySQL中的隐式转换
  8. 详解MySQL 8.0 之不可见索引
  9. 实例验证MySQL|update字段为相同的值是否
  10. mysql 8.0.22 安装配置方法图文教程