1.基本信息
属性动画的优点
不再局限于View对象,无对象也可以进行动画处理
不再局限于四种基本变换:平移,旋转,缩放,透明度
可以灵活的操作任意对象属性,根据自己的业务来实现自己想要的结果
核心点
ObjectAnimator 对象动画
ValueAnimator 值动画
PropertyValueHolder 用于同时执行多个动画
TypeEvaluator 估值器
AnimatorSet 动画集合
Interpolator 差值器
2.代码示例
使用ObjectAnimator完成在一段时间内透明度的变化
/**
* ObjectAnimator基本使用继承子ValueAnimator
* 对对象v的alpha参数进行操作,alpha的值从1.0变到0.3
*
* @param v
*/
public void startObjectAnimatorAnim(View v) {
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0.3f);
//执行事件
alphaAnim.setDuration(1000);
//延迟
alphaAnim.setStartDelay(300);
alphaAnim.start();
}
使用ValueAnimator完成在一段事件内缩放
/**
* 在一段时间内生成连续的值完成view的缩放
* @param v
*/
public void startValueAnimatorAnim(final View v) {
//不改变属性大小,只在一段事件内生成连续的值
ValueAnimator animator = ValueAnimator.ofFloat(0f, 100f);
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//百分比对应的值
float value = (float) animation.getAnimatedValue();
Log.e("TAG", "onAnimationUpdate: " + value);
v.setScaleX(0.5f + value / 200);
v.setScaleY(0.5f + value / 200);
}
});
animator.start();
}
使用PropertyValueHolder完成上面的俩个动画同时执行
/**
* 一个动画实现多个效果的变换
*
* @param v
*/
public void startPropertyValueHolderAnim(View v) {
PropertyValuesHolder alphaProper = PropertyValuesHolder.ofFloat("alpha", 0.5f, 1f);
PropertyValuesHolder scaleXProper = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1f);
PropertyValuesHolder scaleYProper = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1f);
ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder(v, alphaProper, scaleXProper, scaleYProper);
animator.setDuration(500);
animator.start();
}
AnimatorSet多个动画按制定顺序执行
/**
* 执行多个动画并控制动画顺序
*
* @param v
*/
public void startAnimatorSet(View v) {
ObjectAnimator animator1 = ObjectAnimator.ofFloat(v, "translationX", 0f, 100f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(v, "scaleX", 0f, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(500);
//动画1,2同时执行
animatorSet.play(animator1).with(animator2);
//动画2执行完成后执行动画3
animatorSet.play(animator3).after(animator2);
animatorSet.start();
}
animatorSet.play(animator1).with(animator2);动画1和动画2同时执行
animatorSet.play(animator3).after(animator2);动画3在动画2执行完成后执行
animatorSet.playSequentially(animator1,animator2,animator3)动画1,2,3按顺序执行
animatorSet.playTogether(animator1,animator2,animator3)三个动画同时执行
估值器使用
估值器可以自定义变换规则,普通动画是匀速执行
/**
* 使用估值器实现重力下落
*
* @param v
*/
public void startEvaluator(final View v) {
ValueAnimator animator = new ValueAnimator();
animator.setDuration(3000);
animator.setObjectValues(new PointF(0, 0));
final PointF pointF = new PointF();
animator.setEvaluator(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
//fraction是运动中的匀速变化的值
//根据重力计算实际的运动y=vt=0.5*g*t*t
//g越大效果越明显
pointF.x = 100 * (fraction * 5);
pointF.y = 0.5f * 300f * (fraction * 5) * (fraction * 5);
return pointF;
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF p = (PointF) animation.getAnimatedValue();
v.setX(p.x);
v.setY(p.y);
}
});
animator.start();
}
插值器
已经定义好计算规则的估值器(API中已经定义好了算法)
//加速查值器,参数越大,速度越来越快
animator.setInterpolator(new AccelerateInterpolator(10));
//减速差值起,和上面相反
animator.setInterpolator(new DecelerateInterpolator(10));
//先加速后减速插值器
animator.setInterpolator(new AccelerateDecelerateInterpolator());
//张力值,默认为2,T越大,初始的偏移越大,而且速度越快
animator.setInterpolator(new AnticipateInterpolator(3));
//张力值tension,默认为2,张力越大,起始时和结束时的偏移越大
animator.setInterpolator(new AnticipateOvershootInterpolator(6));
//弹跳插值器
animator.setInterpolator(new BounceInterpolator());
//周期插值器
animator.setInterpolator(new CycleInterpolator(2));
//线性差值器,匀速
animator.setInterpolator(new LinearInterpolator());
3.实战
使用属性动画完成下面效果
image
1.拆分动画
刚进来时执行旋转动画
数据加载完毕之后调用聚合逃逸动画
聚合逃逸动画完成之后,进行扩散
使用策略模式控制状态转变,一种状态控制一个动画
/**
* 策略模式(状态模式),每一种状态是一个绘制
*/
private abstract class SplashState {
public abstract void drawState(Canvas canvas);
public void cancle() {
}
}
/**
* 旋转动画
*/
private class RotateState extends SplashState {
@Override
public void drawState(Canvas canvas) {
}
}
/**
* 聚合动画
*/
private class MerginState extends SplashState {
@Override
public void drawState(Canvas canvas) {
}
}
/**
* 扩散动画
*/
private class ExpandState extends SplashState {
@Override
public void drawState(Canvas canvas) {
}
}
画圆及完成旋转动画
使用值动画计算360度旋转过程的中间值,记录当前旋转的角度
/**
* 旋转动画
*/
private class RotateState extends SplashState {
public RotateState() {
//0-360度
mAnimator = ValueAnimator.ofFloat(0f, (float) (Math.PI * 2));
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentRotationAngle = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mAnimator.setDuration(mRotationDuration);
//无限循环
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
@Override
public void drawState(Canvas canvas) {
drawBackground(canvas);
drawCircles(canvas);
}
}
通过计算每个小圆的初始角度及大圆旋转的角度得出每个小圆旋转过程中的角度
根据每个小圆所在的角度,根据勾股定理计算每个小圆的圆心位置
画出每个小圆
/**
* 画圆
*/
private void drawCircles(Canvas canvas) {
//x=r*cons(a)+centerX
//y=r*sina(a)+centerY
//1.周长
float rotationAngle = (float) (2 * Math.PI / mCircleColors.length);
for (int i = 0; i < mCircleColors.length; i++) {
//计算每个小圆的角度
double angle = i * rotationAngle + mCurrentRotationAngle;
//计算每个小圆的圆心
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);
}
}
image
实现聚合动画
实现:改变大圆的半径(根据值动画值的变化改变半径大小),使用张力差值起完成一次反弹效果
/**
* 聚合动画
*/
private class MerginState extends SplashState {
public MerginState() {
mAnimator = ValueAnimator.ofFloat(mRotationRadius, 0);
mAnimator.setDuration(mRotationDuration);
//张力插值器,扩散反弹一下
mAnimator.setInterpolator(new OvershootInterpolator(10));
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentRotationRadius = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mAnimator.start();
}
@Override
public void drawState(Canvas canvas) {
drawBackground(canvas);
drawCircles(canvas);
}
}
延迟5s执行splashDisappear完成状态的转换
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mState == null) {
mState = new RotateState();
}
//先绘制旋转
mState.drawState(canvas);
}
/**
* 进行状态转换
*/
public void splashDisappear() {
if (mState != null && mState instanceof RotateState) {
mState.cancle();
post(new Runnable() {
@Override
public void run() {
mState = new MerginState();
}
});
}
}
实现扩散动画
实现:创建一个空心圆,初始为小圆大小,最终直径等于对角线大小,线条宽度等于对角线一半减去值动画的中间值
image
/**
* 扩散动画
*/
private class ExpandState extends SplashState {
public ExpandState() {
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 drawState(Canvas canvas) {
drawBackground(canvas);
}
}
/**
* 画背景
*
* @param canvas
*/
private void drawBackground(Canvas canvas) {
if (mHoleRadius > 0) {
float strokeWidth = mDiagonalDist - mHoleRadius;
mPaintBackground.setStrokeWidth(strokeWidth);
float radius = mHoleRadius + strokeWidth / 2;
canvas.drawCircle(mCenterX, mCenterY, radius, mPaintBackground);
} else {
canvas.drawColor(mSplashBgColor);
}
}
最终效果如下
image
Demo地址