Android-Property Animation(属性动画)

一、Property动画简介:
    Property动画系统是一个很强大的框架,几乎可以让任何东西产生动画效果。可以定义一个动画随着时间的推移来改变任何对象的属性,无论它被绘制到屏幕上与否。其主要的Animator有:ValueAnimator、ObjectAnimator和AnimatorSet。
   Property动画系统允许定义一个动画的下列属性:
   1)Duration:动画持续时间,默认值为300ms。  
    2)Time interpolation:时间插值你可以把属性的值如何被计算指定为动画当前逝去时间的函数。
    3)Repeat count and behavior:重复次数和行为。你可以指定是否让一个动画重复当它到达持续时间的结束处时,以及重复动画多少次。你还可以指定你是否希望动画反向回放。设置它以向前然后向后地重复反向播放动画,直至到达重复次数。
    4)Animator sets:动画集,你可以分组动画进逻辑集合,它们同时地或依次地或在指定延迟后播放。
    5)Frame refresh delay:帧刷新延迟,可以指定多久刷新动画的帧,默认是设置每隔10ms刷新,但你的应用程序刷新帧的速度最终依赖于系统整体有多繁忙,以及系统可以多快地提供底层定时器。

二、Property动画如何工作:
    这里用一个简单的列子来说明,假如有一个对象在X轴上利用动画使其匀速移动,那么X就相当于此对象的一个property,设定每隔10ms刷新一次,总共移动40ms。那么到移动动画结束时,此对象在此过程中是如何表现的?如图:    
    当然也可以让此动画过程非匀速移动,就如View动画里面的匀速或者先匀速再加速然后再匀速一样(匀速对应linearInterpolator,后者对应accelerateDecelerateInterpolator):
      
    那么对于上面的列子,属性动画的实现有哪些重要组成部分,下图就很好描述了这点,并展示了主要类如何与另一个类工作的。
  
    ValueAnimator跟踪动画的时间,比如:动画运行了多长时间。ValueAnimator封装了TimeInterpolator,它定义动画的插值(interpolation)和TypeEvaluator,TypeEvaluator定义了如何计算被实现动画的属性的值。这部分可以查看源码:
    ...
// The time interpolator to be used if none is set on the animation
    private static final TimeInterpolator sDefaultInterpolator =
            new AccelerateDecelerateInterpolator();
    ...
 private TimeInterpolator mInterpolator = sDefaultInterpolator;
    ...
 /**
     * The time interpolator used in calculating the elapsed fraction of this animation. The
     * interpolator determines whether the animation runs with linear or non-linear motion,
     * such as acceleration and deceleration. The default value is
     * {@link android.view.animation.AccelerateDecelerateInterpolator}
     *
     * @param value the interpolator to be used by this animation. A value of <code>null</code>
     * will result in linear interpolation.
     */
    @Override
    public void setInterpolator(TimeInterpolator value) {
        if (value != null) {
            mInterpolator = value;
        } else {
            mInterpolator = new LinearInterpolator();
        }
    }
三、属性动画和View动画有什么不同:
    1)View动画仅支持View对象,而让View对象实现动画的同时并不能让view对象的背景颜色等也发生变换。
    2)View动画的另一个缺点是:它只改变视图被绘制的部分,而不是实际的视图本身。举个例子,如果你实现一个动画,让一个Button在屏幕上移动,Button是被正确的绘制,但是你能点击Button的地方却没有变化。为此,你还必须实现自己的逻辑来解决这个问题。
    3)对于属性动画,上面提到的那些限制就没有了,可以为属性动画设置任何对象(包括视图和非视图对象)并且对象的任何属性也随着变换。属性动画在其执行动画的方式上也更加强大。在一个较高的水平,指定动画给你想要实现动画的属性,如:颜色、位置和大小,并可以定义动画的各个方面,如插值和多动画同步。
    4)属性动画需要较少的时间来建立动画,并且需要更少的代码来实现。如果视图动画完成了你需要做的任何事,或者你现有的代码已经能按照你想要的方式工作,那么就没必要使用属性动画,当然,可以根据不同的条件使用这两种动画。

四、API概要:
    1)Animators: ValueAnimator、ObjectAnimator和AnimatorSet。
    2)Evaluators:       
Class/Interface Description
IntEvaluator 计算int属性值的默认evaluator 
FloatEvaluator 计算float属性值的默认evaluator 
ArgbEvaluator 计算color属性值的默认evaluator,为16进制。
TypeEvaluator 一个接口,允许你创建自己的Evaluator,如果你实现对象动画的属性不是int,float或者color,你必须实现此接口,用来指定如何计算对象属性的动画值,当然,你也可以为int,float和color自定义TypeEvaluator,如果你想进行的不同于这些types的默认行为。
    3)Interpolators:可以参考Android-View Animation(视图动画)。相对于View动画,多了一个TimeInterpolator插值器(一个接口,允许你实现自己的Interpolator)。

五、属性动画实现体验:
    1)使用ValueAnimator实现动画:
      ValueAnimator提供了3个方法来获得一个ValueAnimator对象:ofInt()、ofFloat()、
ofObject()。以ofFloat为例来实现一个属性动画:    
private void startValueAnimation(){
    if(mValueAnimator == null){
        mValueAnimator = ValueAnimator.ofFloat(0, 500);
    }
    mValueAnimator.setInterpolator(new AnticipateInterpolator());
    mValueAnimator.setTarget(mImageView);
    mValueAnimator.setDuration(3000);
    mValueAnimator.setRepeatCount(1);
    mValueAnimator.start();
    mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mImageView.setTranslationX((Float) animation.getAnimatedValue());
            mImageView.setTranslationY((Float) animation.getAnimatedValue());
        }
    });
}
 
 注意:setRepeatCount()方法,设置为1的时候,动画重复是2次,因为默认是0,表示只重复一次。那么设置1的时候就是重复2次,具体逻辑可看源码。
  2)使用ObjectAnimator实现动画:  
private void startObjectAnimation() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mImageView, "translate", 0, 500);
    objectAnimator.setInterpolator(new AnticipateInterpolator());
    objectAnimator.setDuration(3000);
    objectAnimator.setRepeatCount(1);
    objectAnimator.start();
    objectAnimator.addUpdateListener(new AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mImageView.setTranslationX((Float) animation.getAnimatedValue());
            mImageView.setTranslationY((Float) animation.getAnimatedValue());
        }
    });
} 

  
  
从代码上看实现同样的动画,ObjectAnimator和ValueAnimator相差不大,而事实上ObjectAnimator是继承于ValueAnimator的。

3)使用AnimatorSet:
  AnimatorSet大多数情况下都是被用来处理多个动画,比如:要播放一个动画,但是这个动画必须在另外一个动画播放结束后才开始,这个时候就需要AnimatorSet登场了。
  举个例子来说明一下:假如要让一个对象移动轨迹呈正方形,就可以先水平向右移动,再垂直向下移动,再水平向左移动,最后垂直向上移动到起始位置,那么这个过程中的先后顺序就可以使用AnimatorSet来控制,如实现代码:    
private void startAnimatorSet(){
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(gethValueAnimatorOne()).before(getvValueAnimatorOne());
    animatorSet.play(getvValueAnimatorOne()).before(gethValueAnimatorTwo());
    animatorSet.play(gethValueAnimatorTwo()).before(getvValueAnimatorTwo());
    animatorSet.start();

}

private ValueAnimator gethValueAnimatorOne(){
    if(hValueAnimatorOne == null) {
        hValueAnimatorOne = ValueAnimator.ofFloat(0, 500);
    }
    hValueAnimatorOne.setInterpolator(new LinearInterpolator());
    hValueAnimatorOne.setTarget(mImageView);
    hValueAnimatorOne.setDuration(2000);
    hValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
            public void onAnimationUpdate(ValueAnimator animation) {
            mImageView.setTranslationX((Float) animation.getAnimatedValue());
        }
    });
    return hValueAnimatorOne;
}

private ValueAnimator getvValueAnimatorOne(){
    if(vValueAnimatorOne == null){
        vValueAnimatorOne = ValueAnimator.ofFloat(0, 500);
    }
    vValueAnimatorOne.setInterpolator(new LinearInterpolator());
    vValueAnimatorOne.setTarget(mImageView);
    vValueAnimatorOne.setDuration(2000);
    vValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
        mImageView.setTranslationY((Float) animation.getAnimatedValue());
        }
    });
    return vValueAnimatorOne;
}

private ValueAnimator gethValueAnimatorTwo(){
    if(hValueAnimatorTwo == null){
        hValueAnimatorTwo = ValueAnimator.ofFloat(500, 0);
    }
    hValueAnimatorTwo.setInterpolator(new LinearInterpolator());
    hValueAnimatorTwo.setTarget(mImageView);
    hValueAnimatorTwo.setDuration(2000);
    hValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mImageView.setTranslationX((Float) animation.getAnimatedValue());
        }
    });
    return hValueAnimatorTwo;
}

private ValueAnimator getvValueAnimatorTwo(){
    if(vValueAnimatorTwo == null){
        vValueAnimatorTwo = ValueAnimator.ofFloat(500, 0);
    }
    vValueAnimatorTwo.setInterpolator(new LinearInterpolator());
    vValueAnimatorTwo.setTarget(mImageView);
    vValueAnimatorTwo.setDuration(2000);
    vValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mImageView.setTranslationY((Float) animation.getAnimatedValue());
        }
    });
    return vValueAnimatorTwo;
}
 效果如图:
 
 当然AnimatorSet不仅可以控制先后,还可以让两个动画同时展现出来,依然使用上面例子,让对象向右移动的时候同时旋转,如实现代码:
private void startAnimatorSet(){
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(gethValueAnimatorOne()).with(getmObjectAnimator());
    animatorSet.play(gethValueAnimatorOne()).before(getvValueAnimatorOne());
    animatorSet.play(getvValueAnimatorOne()).before(gethValueAnimatorTwo());
    animatorSet.play(gethValueAnimatorTwo()).before(getvValueAnimatorTwo());
    animatorSet.start();
}
...
private ObjectAnimator getmObjectAnimator(){
    if(mObjectAnimator == null){
        mObjectAnimator = ObjectAnimator.ofFloat(mImageView, "rotate", 0, 360);
    }
    mObjectAnimator.setDuration(2000);
    mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mImageView.setRotation((Float) animation.getAnimatedValue("rotate"));
        }
    });
    return mObjectAnimator;
}
 (图1)
上面配置动画的属性都是在code中配置的,当然也可以使用xml,这个就不再多说了。如:
<setandroid: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> 
AnimatorSetset=(AnimatorSet)AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
    set.setTarget(myObject);
    set.start();

六、动画的监听-Animator.AnimatorListener():
 
 
    回调方法主要有:onAnimationStart、onAnimationEnd、onAnimationCancel、onAnimationRepeat。通过实现这些方法,可以在动画不同的状态下做一些事情。依旧举个例子来测试下。就如上面图1中的效果。上面使用AnimatorSet来实现的,如果不用AnimatorSet是否可以实现?此时就需要这些接口来实现了。在监听到当前动画结束时,在对应的回调中去开始下一个动画。具体代码如下:
  
  
private void startAnimatorSet(){
        gethValueAnimatorOne().start();
    }
    private ValueAnimator gethValueAnimatorOne(){
        if(hValueAnimatorOne == null) {
            hValueAnimatorOne = ValueAnimator.ofFloat(0, 500);
        }
        hValueAnimatorOne.setInterpolator(new LinearInterpolator());
        hValueAnimatorOne.setTarget(mImageView);
        hValueAnimatorOne.setDuration(2000);
        hValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mImageView.setTranslationX((Float) animation.getAnimatedValue());
            }
        });
        hValueAnimatorOne.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                getmObjectAnimator().start();
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                getvValueAnimatorOne().start();
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        return hValueAnimatorOne;
    }
    private ValueAnimator getvValueAnimatorOne(){
        if(vValueAnimatorOne == null){
            vValueAnimatorOne = ValueAnimator.ofFloat(0, 500);
        }
        vValueAnimatorOne.setInterpolator(new LinearInterpolator());
        vValueAnimatorOne.setTarget(mImageView);
        vValueAnimatorOne.setDuration(2000);
        vValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mImageView.setTranslationY((Float) animation.getAnimatedValue());
            }
        });
        vValueAnimatorOne.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                gethValueAnimatorTwo().start();
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        return vValueAnimatorOne;
    }
    private ValueAnimator gethValueAnimatorTwo(){
        if(hValueAnimatorTwo == null){
            hValueAnimatorTwo = ValueAnimator.ofFloat(500, 0);
        }
        hValueAnimatorTwo.setInterpolator(new LinearInterpolator());
        hValueAnimatorTwo.setTarget(mImageView);
        hValueAnimatorTwo.setDuration(2000);
        hValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mImageView.setTranslationX((Float) animation.getAnimatedValue());
            }
        });
        hValueAnimatorTwo.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                getvValueAnimatorTwo().start();
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        return hValueAnimatorTwo;
    }
    private ValueAnimator getvValueAnimatorTwo(){
        if(vValueAnimatorTwo == null){
            vValueAnimatorTwo = ValueAnimator.ofFloat(500, 0);
        }
        vValueAnimatorTwo.setInterpolator(new LinearInterpolator());
        vValueAnimatorTwo.setTarget(mImageView);
        vValueAnimatorTwo.setDuration(2000);
        vValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mImageView.setTranslationY((Float) animation.getAnimatedValue());
            }
        });
        return vValueAnimatorTwo;
    }
    private ObjectAnimator getmObjectAnimator(){
        if(mObjectAnimator == null){
            mObjectAnimator = ObjectAnimator.ofFloat(mImageView, "rotate", 0, 360);
        }
        mObjectAnimator.setDuration(2000);
        mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mImageView.setRotation((Float) animation.getAnimatedValue("rotate"));
            }
        });
        return mObjectAnimator;
    }

最后,这里使用的动画例子比较简单,如果需要更多的练习,可以看看ApiDemo中列子,再结合Genymotion模拟上的ApiDemo看效果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值