属性动画打造,逃逸,聚合,扩散实例


要求要实现效果:


https://img-blog.csdn.net/20180711144858825?watermar.gift


要如何实现这个效果呢???


我们要从以下几个方面考虑,一步一步来:


1.考虑如何绘制6个等间距的颜色圆圈。

2.如何实现旋转动画效果。

3.如何实现小圆逃逸后在聚合。

4.如何实现水波纹扩散。


我们知道实现思路,接下来我按着我们的思路一步一步的实现:


一.初始化数据


private void init(Context context) {
    mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);

    //设置取消锯齿
    mPaint.setAntiAlias(true);
    mPaintBackground.setAntiAlias(true);
    //设置边框样式
    mPaintBackground.setStyle(Style.STROKE);
    mPaintBackground.setColor(mSplashBgColor);
}

以上代码:获取我们要绘制的颜色及设置Paint属性。

 
 

二.绘制等间距的小圆

/**
 * 画多个小圆
 *
 * @param canvas
 */
public void drawCircle(Canvas canvas) {
    //每个小圆的间隔角度
    float rotationAngle = (float) (Math.PI * 2 / mCircleColors.length);

    for (int i = 0; i < mCircleColors.length; i++) {
        //每个小圆i*间隔角度+旋转角度=当前小圆的真实角度
        double angle = mCurrentRotationAngle + i * rotationAngle;

        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);
    }
}


三.实现旋转动画


public RotationState() {
    //小圆半径,需要大圆半径和它旋转的角度
    //估值器--它使用的是弧度0~2PI
    mAnimator = ValueAnimator.ofFloat(0, (float) Math.PI * 2);
    //线性插值器,会平滑地计算;这样在每完成一个周期时,它不会卡顿
    mAnimator.setInterpolator(new LinearInterpolator());
    //设置旋转时间
    mAnimator.setDuration(mRotationDuration);
    mAnimator.addUpdateListener(new AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            //这个mCurrentRotationAngle这个值的变化,就是造成小圆旋转的原因;若
            //这个值不变了,则小圆们就不会旋转
            mCurrentRotationAngle = (Float) animation.getAnimatedValue();
            //提醒view重绘
            invalidate();
        }
    });

    //设置旋转次数--无穷次数;因为它不知道什么时候进入下一个动画,所以把它设置为重复次数为无穷
    mAnimator.setRepeatCount(ValueAnimator.INFINITE);
    mAnimator.start();
}

到此你会有个疑问,旋转动画是怎么实现的,接着我们在这里发现了ValueAnimator.ofFloate()这个方法,这个方法干了什么,带着疑问继续往下看。


/**
 * Constructs and returns a ValueAnimator that animates between float values. A single
 * value implies that that value is the one being animated to. However, this is not typically
 * useful in a ValueAnimator object because there is no way for the object to determine the
 * starting value for the animation (unlike ObjectAnimator, which can derive that value
 * from the target object and property being animated). Therefore, there should typically
 * be two or more values.
 *
 * @param values A set of values that the animation will animate between over time.
 * @return A ValueAnimator object that is set up to animate between the given values.
 */
public static ValueAnimator ofFloat(float... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setFloatValues(values);
    return anim;
}

上面代码:构造并返回一个ValueAnimator,它在浮动值之间进行动画。也就是我们上面传入的参数0~Math.PI*2 ==0~2π,也就是一个圆周长之间进行动画。浮动值之间进行动画是什么意思???接下来继续分析:



/**
 * Sets float values that will be animated between. A single
 * value implies that that value is the one being animated to. However, this is not typically
 * useful in a ValueAnimator object because there is no way for the object to determine the
 * starting value for the animation (unlike ObjectAnimator, which can derive that value
 * from the target object and property being animated). Therefore, there should typically
 * be two or more values.
 *设置浮动值,在动画之间。一个单一的数值意味着这个值是一个动画。然而,这并不是典型的
  在ValueAnimator对象中有用,因为对象无法确定
  动画的起始值(不像ObjectAnimator,它可以派生出这个值
  从目标对象和属性被激活)。因此,ValueAnimator应该一般是两个或更多的值。
 * <p>If there are already multiple sets of values defined for this ValueAnimator via more
 * than one PropertyValuesHolder object, this method will set the values for the first
 * of those objects.</p>
 *
 * @param values A set of values that the animation will animate between over time.
 */
public void setFloatValues(float... values) {
    if (values == null || values.length == 0) {
        return;
    }
    if (mValues == null || mValues.length == 0) {
        setValues(PropertyValuesHolder.ofFloat("", values));
    } else {
        PropertyValuesHolder valuesHolder = mValues[0];
        valuesHolder.setFloatValues(values);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}


在这里我们发现了浮动值,在动画之间一个值对应一个动画。在这里我们看注释也发现了ValueAnimator与ObjectAnimator的区别。


四.实现小圆逃逸后在聚合


mAnimator = ValueAnimator.ofFloat(0, mRotationRadius);
//插值器-弹射效果的,tension表示弹射的范围长度
mAnimator.setInterpolator(new OvershootInterpolator(60f));
//设置动画时间
mAnimator.setDuration(mSplashDuration / 3);
mAnimator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mCurrentRotationRadius = (Float) animation.getAnimatedValue();//1
        //提醒view重绘
        invalidate();
    }
});

//监听动画执行完毕状态
mAnimator.addListener(new AnimatorListenerAdapter() {  //2
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        //进入下一个动画
        mState = new ExpandingState();
    }
});
//反向调用的点向后播放
mAnimator.reverse();

上面代码:setInterpolator(new OvershootInterpolator(60f));表示向外弹射的范围,
上面注释1获取所有的动画值,通过invalidate()方法不断绘制也就是收缩。注释2监听动画状态,
如果动画执行完毕就接着做后续工作,也就是我们接下来要说的扩散动画。


五.扩散动画


        public ExpandingState() {
            //估值器 --空心圆的半径:0到对角线的一半
            mAnimator = ValueAnimator.ofFloat(0, mDiagonalDist);
            //设置动画时间
            mAnimator.setDuration(mSplashDuration / 3);
            mAnimator.addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //空心圆半径
                    mHoleRadius = (Float) animation.getAnimatedValue();
                    //提醒view重绘
                    invalidate();
                }
            });


            mAnimator.start();
        }

        @Override
        public void drawState(Canvas canvas) {
      
            //绘制空心圆效果
            if (mHoleRadius > 0f) {
                //画笔的宽度:对角线的一半 - 空心圆半径
                float storkeWidth = mDiagonalDist - mHoleRadius;
                mPaintBackground.setStrokeWidth(storkeWidth);
                //为了看的清楚动画扩散,storkeWidth设置为10
//                mPaintBackground.setStrokeWidth(10);

                float circleRadius = mHoleRadius + mDiagonalDist / 2f;
                //绘制扩散圆的效果
                canvas.drawCircle(mCenterX, mCenterY, circleRadius, mPaintBackground);
            }
        }


代码链接地址:https://github.com/yangxiansheng123/PropertyAnimation


参考文章:


1.https://blog.csdn.net/NaoiAyato/article/details/78290359

2.https://blog.csdn.net/qq_18983205/article/details/78154900


Project中用到的其他动画库:


1.Android水波动画帮助类

2.遮罩系统设置开始和结束时的动画效

3.高仿新版58 加载动画

3.一个带渐变层叠动画的左右滑动效果(类似于探探、tinder)

4.用于做Path动画的自定义View

5.一款加载动画,不同的物体加速下落砸到了文字上,文字逞阻尼效果,向下弯曲并像橡皮筋一样弹动回去的效果

6.知乎 Android 客户端启动页的视差动画效果实现

7.多达288种动画效果定制的侧滑菜单库

8.下拉刷新和上拉加载更多的RecyclerView,具有下拉和刷新动画。

9.数字动画 TextView

10.Android 动画各种实现,包括帧动画、补间动画和属性动画

11.各种pulltorefresh动画的合集

12.炫酷的相册动画合集,集合了粒子、雪花、气泡、蝴蝶心形路径、星星、相册翻页等效果




猜你喜欢

转载自blog.csdn.net/u014133119/article/details/80998025
今日推荐