欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

属性动画(Property Animation)

程序员文章站 2022-03-16 16:13:21
...

内容如下:

  • 属性动画与补间动画的不同
  • 属性动画工作原理
  • API 概述
  • 计算器 Evaluators
  • 插值器 Interpolators
  • 使用ValueAnimator
  • 使用ObjectAnimator
  • AnimatorSet
  • 动画监听
  • 视图容器中布局的动画改变
  • 使用TypeEvaluator
  • 使用插值器 (Interpolators)
  • 关键帧
  • ViewPropertyAnimator
  • 声明XML

属性动画与补间动画的不同

补间动画只提供了运动可视对象的能力,如果你想操作非可视对象,就不得不自己用代码实现。实际上,补间动画具有局限性,它只能够使一个视图对象的几个方面做运动,比如:视图的缩放、旋转,而不能操作背景颜色等等。

补间动画另一个缺点是:它只能够改变视图绘制的位置,并不能改变它的真实位置。例如:你在屏幕中运动一个按钮,按钮将被正确的绘制,但是当你点击当前位置的按钮时,按钮并不会有任何改变。所以你不得不通过实现自己的逻辑来操作它。

对于属性动画而言,这些限制被全部移除。你能够使任何对象(可视和非可视)的任何属性运动,并且对象自身被修改。属性动画就是通过不停修改属性值实现动画效果的。属性动画在实现动画方面更加强大。你可以根据需求将动画分派给对象的属性,比如:颜色、位置、大小;你也可以定义动画的差值器以及多动画的同步。

不管怎样,补间动画的创建可以用更少的时间和更少的代码。如果补间动画能够完成你需要的每件事,或者代码中已存在的补间动画代码能够实现你的需求。你就没必要使用属性动画。通过不同的使用场景来确定使用两种动画系统的哪一种。


属性动画工作原理

图1描述了一个假想的对象,沿x轴运动,表示它在屏幕上的水平位置。在40ms内运动40px,每10毫秒(默认帧刷新率),对象水平移动10像素。到40ms时,动画结束。对象停在40px处。这是一个线性插值动画的例子,表示对象匀速运动。

属性动画(Property Animation)

还可以指定具有非线性插值的动画。图2假设一个对象,在动画开始时加速,然后减速动画结束时。该对象仍然在40ms移动40px,但非线性。

属性动画(Property Animation)

属性动画系统的重要组件如何计算上面提到的动画:
属性动画(Property Animation)

ValueAnimator 对象保持跟踪动画的时间,比如动画的已运动时间和动画的当前值。
TimeInterpolator 定义动画的插值器。
TypeEvaluator 定义如何计算动画属性的值。

开始一个动画:

  • 创建一个ValueAnimator并给它的开始属性值、结束属性值以及动画的持续时间。
  • 调用start()开始动画。
  • ValueAnimator计算一个在0和1之间的逝去分数,基于动画的持续时间和经过的多少时间。该分数表示动画完成的时间百分比,0表示0%,1表示100%。
  • 当ValueAnimator完成计算一个逝去分数,它调用当前设置的TimeInterpolator计算插值分数。
  • 当插值分数计算完成,ValueAnimator调用合适的TypeEvaluator,计算该属性的值,基于插值分数、起始值和动画结束值。

API 概述

  • ValueAnimator 属性动画的计时引擎,通过属性值进行动画处理。包括计算动画值的核心功能,动画随时间变化的细节,动画重复监听以及更新时间,自定义evaluate。属性动画包括两部分:1.计算动画值;2.对正在进行动画的对象设置这些值。ValueAnimator不能直接完成第二部分,所以必须在UpdateListener中更新属性值。

  • ObjectAnimator ValueAnimator的子类,可以直接设置目标对象和属性进行动画。ObjectAnimator使用起来更方便,也有限制,对于进行的动画的属性必须要有setter,getter的访问方法。

  • AnimatorSet 提供一种将多动画分组并使之关联运行的机制。可以使动画同时执行,顺序执行,延时执行。


估算器 Evaluators

  • IntEvaluator 计算int型属性
  • FloatEvaluator 计算float型属性
  • ArgbEvaluator 计算16进制的颜色值
  • TypeEvaluator 自定义计算器(更多见下文)

插值器 Interpolators

  • AccelerateDecelerateInterpolator 先加速后减速
  • AccelerateInterpolator 加速
  • AnticipateInterpolator 向相反运动一小段后再开始运行
  • AnticipateOvershootInterpolator 前后都超出一小段
  • BounceInterpolator 结束时振动结束
  • CycleInterpolator 往返运行动画
  • DecelerateInterpolator 减速运行
  • LinearInterpolator 匀速运行
  • OvershootInterpolator 结束时超出一小段
  • TimeInterpolator 自定义

使用ValueAnimator

示例:使用ValueAnimator做曲线运动

通过调用ofInt(),ofFloat(),ofObject()方法获得一个ValueAnimator,如下:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

还可以通过以下操作指定一个自定义类型来进行动画:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, 
 endPropertyValue);
animation.setDuration(1000);
animation.start();

上述代码,当调用start()时,ValueAnimator 将在1s内计算0-1之间的动画值。它只是在计算一个值并没有影响到一个具体的对象。你需要实现ValueAnimator.AnimatorUpdateListener这个接口,通过调用getAnimatedValue()方法可以获得特定帧的计算值,通过这个值可以对目标对象进行需要的变换。

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
    // You can use the animated value in a property that uses the
    // same type as the animation. In this case, you can use the
    // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

使用ObjectAnimator

示例:卫星菜单

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

拥有setter,getter方法的属性都可以进行动画操作。
经常操作的属性:

“alpha” 透明度;

“translationY” 沿Y轴平移;

“translationX” 沿X轴平移;

“scaleX” 横向缩放;

“scaleY” 纵向缩放;

“rotation” 平面旋转;

“rotationX” 关于X轴旋转;

“rotationY” 关于Y轴旋转;


AnimatorSet

你可以使用AnimatorSet控制多个Animator的播放顺序,同时播放,顺序播放,谁在前谁在后等。可以嵌套使用。例:

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

常用方法:
playSequentially (Animator…) 顺序播放;
playTogether (Animator…) 一起播放;

Builder方式:
play(Animator a);
创建一个Builder对象;

例1:有四个动画anim1, amin2, anim3,anim4。anim1和anim2同时播放;当anim2播放结束时播放anim3;当anim3播放结束时播放anim4。

 AnimatorSet s = new AnimatorSet();
 s.play(anim1).with(anim2);
 s.play(anim2).before(anim3);
 s.play(anim4).after(anim3);

动画监听

Animator.AnimatorListener
* onAnimationStart() 动画开始时
* onAnimationEnd() 动画结束时
* onAnimationRepeat() 动画重复时
* onAnimationCancel() 动画取消时,同时调用onAnimationEnd()

ValueAnimator.AnimatorUpdateListener
* onAnimationUpdate() 每帧动画都将被调用
通过getAnimatedValue()方法获取动画值。

AnimatorListenerAdapter

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
    public void onAnimationEnd(Animator animation) {
        balls.remove(((ObjectAnimator)animation).getTarget());
    }
)};

视图容器中布局的动画改变

使用TypeEvaluator

如果你想以系统未知的方式运动,你可以通过实现TypeEvaluator接口来创建。实现TypeEvaluator接口只需要实现一个方法evaluate()。如下:

public class FloatEvaluator implements TypeEvaluator {
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

例:使TextView沿正弦曲线运动

创建包装类:

   private class TextHolder
        private TextView view;
        public TextHolder(TextView textView){
            view=textView;
        }
        public XYHolder getXY() {
            return new XYHolder(view.getX(),view.getY());
        }

        public void setXY(XYHolder xyHolder) {
            view.setX(xyHolder.getX());
            view.setY(xyHolder.getY());
        }
    }

估值器:

public class SineTypeEvaluator implements TypeEvaluator<XYHolder> {
    @Override
    public XYHolder evaluate(float fraction, XYHolder startValue, XYHolder endValue) {
        float xHolder=startValue.getX()+fraction*(endValue.getX()-startValue.getX());
        float yHolder=(float) (300*Math.sin(xHolder/100)+endValue.getY());
        return new XYHolder(xHolder,yHolder );
    }
}

开始动画:

ObjectAnimator objectAnimator=ObjectAnimator.ofObject(new TextHolder(mText1),"xY",new 
SineTypeEvaluator(),new XYHolder(0f,400f),new XYHolder(628f,400f));
        objectAnimator.setDuration(2000);
        objectAnimator.start();

使用插值器

一个随时间计算动画值的函数

AccelerateDecelerateInterpolator

public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

public float getInterpolation(float input) {
    return input;
}

关键帧

例如:

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);

ViewPropertyAnimator

ViewPropertyAnimator提供一种更简单的多属性运动的方式。如下对比:

使用多个ObjectAnimator:

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

使用一个ObjectAnimator:

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

使用ViewPropertyAnimator:

myView.animate().x(50f).y(100f);</pre>

声明XML

将xml文件保存在res/animator/ 目录下

动画类与xml标签的对应关系:
ValueAnimator -
ObjectAnimator -
AnimatorSet-

下面的示例依次播放两组对象动画,第一个动画集合同时播放两个对象动画:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

运行上述动画:

AnimatorSet set = (AnimatorSet) 
    AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);
set.setTarget(myObject);
set.start();

在XML中声明ValueAnimator:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

ValueAnimator必须实现AnimatorUpdateListener,如下:

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
    R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});
xmlAnimator.start();
相关标签: 属性动画