30、Android--属性动画

属性动画

由于基本的动画存在局限性,它只改变显示,并不能响应事件,所以在Android 3.0之后,加入了属性动画。

ObjectAnimation

创建ObjectAnimation只需要通过静态工厂类直接返回一个ObjectAnimation对象。参数必须包括一个对象和对象的属性名字,属性必须具备get和set方法。

内部会通过反射机制来调用set方法修改对象的属性值。同样,我们也可以通过setInterpolator设置相应插值器。

/*
 * Object target,           需要操作的View
 * String propertyName,     需要操作的属性
 * float... values          可变参数,需要传递进去该属性变化的取值过程。
 */
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "translationX", 300);
animator.setDuration(1000);
animator.start();

在使用ObjectAnimation时,要操纵的属性必须具备get和set方法,不然则会无效。下面是常见的属性值:

属性 说明
tanslationX和tanslationY 作为一种增量来控制着View对象从它布局容器的左上角坐标偏移的位置。
rotation、rotationX和rotationY 控制着View对象围绕指点进行2D和3D旋转。
scaleX和scaleY 控制着View对象围绕它的支点进行2D缩放。
pivotX和pivotY 控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。
x 和 y 描述View对象在它的容器中的最终位置,它是左上角坐标和tanslationX和translationY值的累积和。
alpha 它表示View对象的alpha透明度,默认值是1(不透明),0代表完全透明。

如果一个属性没有get和set方法,我们则有两种方案来解决该问题,自定义一个属性类或包装类来间接地给这个属性增加get和set方法。

public class WrapperView {
    private View mTarget;
    
    public WrapperView(View target){
        mTarget = target;
    }
    
    public int getWidth(){
        return mTarget.getLayoutParams().width;
    }
    
    public void setWidth(int width){
        mTarget.getLayoutParams().width = width;
        mTarget.requestLayout();
    }
}  

通过以上代码就给属性包装一层,并给它提供get、set方法。使用时只需要操纵包装类就可以间接调用get、set方法了。

WrapperView wrapper = new WrapperView(mButton);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();  

属性动画拓展

两种动画集

  • PropertyValuesHolder

    类似基本动画中的AnimationSet,在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。

PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(mButton, pvh1, pvh2, pvh3).setDuration(1000).start();  
  • AnimatorSet

    对于一个属性同时作用多个属性的动画效果,除了PropertyValuesHolder外,还可以使用AnimatorSet更精准的控制顺序。

ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1, animator2, animator3);  

在属性动画中AnimatorSet正是通过playTogether()、playSwquentinally()、animSet.play.with()、befor()、after、这些方法来控制

多个动画的协同工作并对动画播放顺序进行精准控制。

ValueAnimator

ValueAnimator在属性动画中是非常重要的,它是整个属性动画的核心所在,ObjectAnimator继承自ValueAnimator。ValueAnimator本身不提供任何动画效果,

它更像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画实现过程。通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值

的变换,从而完成动画的变换。

ValueAnimator animator = ValueAnimator.ofFloat(0,100);
animator.setTarget(mButton);
animator.setDuration(1000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float value = (Float) animation.getAnimatedValue();
        //TODO use the value
    }
});  

动画的监听

一个完整的动画具有Start、Repeat、End、Cancel四个过程,通过Android提供了接口,可以方便地监听到这四个事件:

ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "alpha", 0.5f);
animator.addListener(new AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        
    }  
    @Override
    public void onAnimationRepeat(Animator animation) {
        
    }
    @Override
    public void onAnimationEnd(Animator animation) {
        
    }
    @Override
    public void onAnimationCancel(Animator animation) {
        
    }
});
animator.start();  

当然,大部分时候我们只关心onAnimationEnd事件,所以Android也提供了AnimatorListenerAdapter来让我们选择必要的事件进行监听

animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        
    }
});  

XML属性动画

属性动画同补间动画一样,也可以定义在XML中,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType">
</objectAnimator>  

在程序中使用XML定义的属性动画也非常的简单:

public void scaleX(View view){
    Animator anim = AnimatorInflater.loadAnimator(this, R.anim.objectscalex);
    anim.setTarget(view);
    anim.start();
}  

简写方式

在Android4.1.2(API 16)时,Google给View增加了animate()方法来直接驱动属性动画,它是属性动画一种简写形式:

view.animate().alpha(0).y(300).setDuration(3000)
.withStartAction(new Runnable() {
    @Override
    public void run() {
    }
}).withEndAction(new Runnable() {
    @Override
    public void run() {
        runOnUiThread(new Runnable() {
            public void run() {
            }
        });
    }
}).start();  

插值器

插值器(Interpolators)是动画中一个非常重要的概念,可以自定义动画变换速率,类似物理中的加速度。其作用主要是控制目标变量的变化值进行对应的变化。

Interpolator用法

补间器,它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果。补间动画就支持Interpolator,属性动画新增TimeInterpolator接口。
TimeInterpolator的实现类如下表所示:

属性 说明
AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速。
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速。
AnticipateInterpolator 开始的时候向后,然后向前甩。
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值。
BounceInterpolator 动画结束的时候弹起。
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线。
DecelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始减速。
LinearInterpolator 在动画开始后以均匀的速率改变。
OvershootInterpolator 向前甩一定值后再回到原来位置。

使用属性动画时,系统默认的Interpolator其实就是一个先加速后减速的Interpolator,对应的实现类就是AccelerateDecelerateInterpolar。
我们也可以修改这一默认属性,将它替换成任意一个系统内置好的Interpolator。

private void startAnimation() {
    Point startPoint = new Point(getWidth() / 2, RADIUS);
    Point endPoint = new Point(getHeight() / 2, getHeight() - RADIUS);
    ValueAnimator animator = ValueAnimator.ofObject(new PointFEvaluator(), startPoint, endPoint);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Point currentPoint = animation.getAnimatedValue();
            invalidate();
        }
    });
    // 使用系统Interpolator实现碰撞反弹效果
    animator.setInterpolator(new BounceInterpolator());
    animator.setDuration(2 * 1000);
    animator.start();
}

Interpolator详解

TimeInterpolator

首先看下TimeInterpolator的接口定义:

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {
    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

接口非常简单,getInterpolation()方法接收一个input参数,该参数会随着动画的运行而不断有规律的变化,且变化范围是0到1。

LinearInterpolator
系统中内置的LinearInterpolator就是一种匀速运动的Interpolator,那么我们来看一下它的源码是怎么实现的

/**
 * An interpolator where the rate of change is constant
 */
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    public LinearInterpolator() {
    }
    public LinearInterpolator(Context context, AttributeSet attrs) {
    }
    public float getInterpolation(float input) {
        return input;
    }
    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}

该方法没有任何逻辑,就是把参数中传递的input值直接返回了,因此fraction的值就是等于input的值的,这就是匀速运动的Interpolator的实现方式。

  • AccelerateDecelerateInterpolator

系统默认情况下使用的是AccelerateDecelerateInterpolator,那么我们看下它的源码:

/**
 * An interpolator where the rate of change starts and ends slowly but
 * accelerates through the middle.
 */
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }
    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
    }
}

此时getInputerpolator逻辑明显复杂了,不再是直接将参数的input返回,二手进行较为复杂的数学运算。

自定义Interpolator

我们已经了解LinearInterpolator和AccelerateDecelerateInterpolator的实现机制,现在我们自定义Interpolator,由于属性动画默认的Interpolator是先减速后加速的过程,

我们可以修改它为先加速后减速的过程

public class CustomTimeInterpolator implements TimeInterpolator {
    @Override
    public float getInterpolation(float input) {
        float result;
        if (input <= 0.5) {
            result = (float) (Math.sin(Math.PI * input) / 2);
        } else {
            result = (float) ((2 - Math.sin(Math.PI * input)) / 2)
        }
        return result;
    }
}

之后我们将自定义的DecelerateAccelerateInterpolator在代码中进行替换:

private void startAnimation() {
    Point startPoint = new Point(getWidth() / 2, RADIUS);
    Point endPoint = new Point(getHeight() / 2, getHeight() - RADIUS);
    ValueAnimator animator = ValueAnimator.ofObject(new PointFEvaluator(), startPoint, endPoint);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Point currentPoint = animation.getAnimatedValue();
            invalidate();
        }
    });
    // 使用自定义的Interpolator
    animator.setInterpolator(new CustomTimeInterpolator());
    animator.setDuration(2 * 1000);
    animator.start();
}

自定义动画

创建自定义动画只需要继承Animation类,并实现applyTransformation方法即可,通常情况下还需要覆盖父类的initalize方法以完成一些初始化操作。

public class MyAnimation extends Animation {    
    /**完成一些初始化操作*/
    @Override
    public void initialize(int width, int height, int parentWidth,
            int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
    }
    
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
    }
}  

applyTransformation类的interpolatedTime是插值器因子,由动画当前完成的百分比和当前时间所对应的插值所计算得来的,取值范围是0 - 1.0。

第二个参数Transformation是矩阵的封装类,一般使用这个类获得当前的矩阵对象

Matrix matrix = t.getMatrix();  

通过改变matrix对象,可以将动画效果实现出来,基本可以实现任意效果。

public class CustomAnimation extends Animation {   
    private int mCenterWidth;
    private int mCenterHeight;
    private float mRotateY = 0.0f;
    private Camera mCamera = new Camera();
    /**完成一些初始化操作*/
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        // 设置默认时长
        setDuration(2000);
        // 动画结束后保留状态
        setFillAfter(true);
        // 设置默认插值器
        setInterpolator(new BounceInterpolator());
        mCenterWidth = width / 2;
        mCenterHeight = height / 2;
    }
    // 暴漏接口 -设置旋转角度
    public void setRotateY(float rotateY){
        mRotateY = rotateY;
    }   
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        Matrix matrix = t.getMatrix();
        mCamera.save();
        // 使用Camera设置旋转的角度
        mCamera.rotateY(mRotateY * interpolatedTime);
        // 将旋转变换作用到matrix上
        mCamera.getMatrix(matrix);
        mCamera.restore();
        // 通过pre方法设置矩阵作用前的偏移量来改变旋转中心
        matrix.preTranslate(mCenterWidth, mCenterHeight);
        matrix.preTranslate(-mCenterWidth, -mCenterHeight);
    }
}  

调用时需要通过对外暴漏的方法来进行设置:

CustomAnimation customAnimation = new CustomAnimation();
customAnimation.setRotateY(30);
view.startAnimation(customAnimation);  
上一篇:Unity笔记--Animator组件


下一篇:Unity—ParticleSystem(粒子系统)与Animator(动画状态机)批量管理器