美文网首页
属性动画(Property Animation)

属性动画(Property Animation)

作者: fengfancky | 来源:发表于2017-07-20 15:38 被阅读130次

    内容如下:

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

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

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

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

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

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


    属性动画工作原理

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

    image.png

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

    image.png

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


    image.png

    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 - <animator>
    ObjectAnimator - <objectAnimator>
    AnimatorSet- <set>

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

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

    相关文章

      网友评论

          本文标题:属性动画(Property Animation)

          本文链接:https://www.haomeiwen.com/subject/jmlchxtx.html