Android动画之属性动画

属性动画可以在以下几个方面对动画进行设置:

  • Duration:动画持续时间,默认是300ms。
  • Time interpolation:插值器,可以指定属性值如何根据动画当前播放时间进行变换。
  • Repeat count and behavior:重复次数和重复方式,正向或者逆向重复。
  • Animator sets:动画集合,可以对动画进行分类组合,指定播放顺序或者播放延时。
  • Frame refresh delay:可以指定多久更新一次动画的帧,默认是10ms更新一次,但是应用更新动画的速度很大程度上取决于系统的繁忙程度,以及系统可以多快响应内含的定时器。

工作原理

这里写图片描述
ValueAnimator这个类记录着动画的计时,还封装了TimeInterpolator和TypeEvaluator两个类。TimeInterpolator用来计算时间到动画执行比例的变换,TypeEvaluator用来计算变化的属性值。

当ValueAnimator调用start()之后,ValueAnimator会根据动画时长和已流逝时间来计算时间流逝百分比。计算完时间流逝百分比后,ValueAnimator就会调用当前的TimeInterpolator来计算插值调整过后的实际百分比,然后根据实际百分比调用TypeEvaluator来计算当前的属性值。

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();
// 监听值的变化
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

ObjectAnimator的使用

ObjectAnimator是ValueAnimator的子类,帮我们把ValueAnimator改变后的值设置到对象上面,省去设置监听的步骤。

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

如果要让ObjectAnimator可以正常更新属性值,必须要满足以下几个条件:

  • 所要设置动画的对象属性必须有对应的setter方法,如果没有的话,可以采取三种方案之一:1. 有权限修改这个类的话直接加一个setter方法 2. 使用一个包装类,包装类包含setter,然后再把设置的值转给目标对象属性。3. 使用ValueAnimator。
  • 如果ObjectAnimator的工厂方法中values…可变参数的个数是一个,那这个参数值默认是动画的结束值,因此该对象属性必须有一个对应的getter方法来获取动画的初始值。
  • 如果有getter,和setter操作的属性类型必须是一样的。
  • 根据执行动画的特定属性或者对象,可能需要调用invalidate()来强制重绘,可以在onAnimationUpdate()回调方法中操作。

使用动画集合播放多个动画

可以使用AnimatorSet来控制多个动画的播放,AnimatorSet可以自己嵌套自己。

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

动画监听

  • Animator.AnimatorListener
    • onAnimationStart():动画开始时调用
    • onAnimationEnd():动画结束时调用
    • onAnimationRepeat():动画重复时调用
    • onAnimationCancel():动画取消时调用,动画取消时也会调用onAnimationEnd()
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate():动画的每一帧都会调用这个回调,可以调用ValueAnimator对象的getAnimatedValue()来获取当前值。

如果不想要实现AnimatorListener中的所有回调,可以继承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());
}

ViewGroup的布局动画

属性动画在给View对象提供动画的同时,也可以用来给ViewGroup的布局变化设置动画。

布局动画是通过LayoutTransition类来实现。布局动画指的是ViewGroup当中View的添加或删除,或者可见性的改变,对相应的View进行动画出现或者消失。其他View也可以动画移动到新的位置上。

通过调用LayoutTransition对象的setAnimator()方法来设置动画,可以选择以下的常量来设置:

  • APPEARING:表示这个动画用来作用在正在显示的View。
  • CHANGE_APPEARING :表示这个动画用来作用在因为其他新显示View而改变位置的View上。
  • DISAPPEARING :表示这个动画用来作用在正在消失的View。
  • CHANGE_DISAPPEARING :表示这个动画用来作用在因为其他新消失View而改变位置的View上。

如果要使用系统默认的布局动画,只要加上animateLaoutChanges=true即可:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

使用TypeEvaluator

系统已经实现的TypeEvaluator包括IntEvaluator、FloatEvaluator、ArgbEvaluator,如果要对自定义类型进行动画,那就要实现TypeEvaluator接口:

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

使用插值器

插值器是用来计算属性值如何随着动画流逝比例进行变化的。系统提供的插值器在android.view.animation包中,如果要自定义插值器可以实现TimeInterpolator接口。

两种系统插值器的实现:
LinearInterpolator:

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

AccelerateDecelerateInterpolator:

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

指定关键帧

一个Keyframe对象用来指定某个时间点的具体属性值,两个Keyframe之间还可以指定interpolator来控制中间的变化行为。

具体使用就是先调用工厂方法获取Keyframe对象,然后调用PropertyValuesHolder的静态方法ofKeyframe()获取PropertyValuesHolder对象,再调用ObjectAnimator的静态ofPropertyValuesHolder()获取ObjectAnimator。

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用来对View的多个属性同时进行动画。组合多个ObjectAnimator也可以进行多属性动画,不过会比较麻烦。

1. 单纯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();
2. 配合ViewPropertyAnimator进行多属性动画
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

另一种更简洁的用法:

myView.animate().x(50f).y(100f);

ObjectAnimator

ObjectAnimator有个特殊的rotateX和rotateY属性,是在三维维度上进行动画的。

在XML中声明动画

为了跟补间动画区分开来,属性动画的XML文件目录是res/animator/。

以下为类及其对应的XML标签:

  • ValueAnimator : <animator>
  • ObjectAnimator : <objectAnimator>
  • AnimatorSet : <set>

XML示例:

<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>

XML动画的使用:

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

ValueAnimator示例:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />
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();

动画对UI性能的影响

更新UI的动画会对UI每一帧的渲染增加额外的工作,是在渲染管道的动画阶段进行的,可以通过启用Profile GPU Rendering来查看动画阶段的性能表现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值