Android 动画总结

属性动画

属性动画作用

为什么需要属性动画

1. 补间动画只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢

2. 比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,
如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的
3. 补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,
然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,
只不过补间动画将这个按钮绘制到了屏幕的右下角而已。


新引入的属性动画机制 已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。

它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性

举例

        TranslateAnimation translateAnim = new TranslateAnimation(0, 100, 0, 100);
        translateAnim.setDuration(1000);
        translateAnim.setFillAfter(true);//被pause的时候,动画效果没有了
        testBut.startAnimation(translateAnim);      
    @OnClick(R.id.test)
    void testClick() {
        Toast.makeText(this, "test click", Toast.LENGTH_SHORT).show();
    }
动画执行完毕之后,testBut停留在原来位置右下偏移100px的位置,但是如果在button当前位置点击button,不会出现toast。

如果在原来位置点击button,才会出现toast。同时button还会返回到原来的位置,这很奇怪,所以以后有必要对动画的原理做一个深入的了解。同时如果startAnim.clearAnimation(),那么button也会回到原来的位置。。

        ObjectAnimator translate = ObjectAnimator.ofFloat(testBut, "translationX", 0, 100);//默认属性都发生了变化,所以不需要setFillAfter
        translate.setDuration(1000);
        translate.start();

如果此时换成属性动画,因为属性动画改变的是view的属性,所以仍然可以在新位置触发点击事件。同时startAnim.clearAnimation()是不生效的。。。



ValueAnimator

属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
它的内部使用一种 时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果.

public static ValueAnimator ofInt(int... values)
public static ValueAnimator ofFloat(float... values)
这两个方法,说明可以传入多个int或者float参数,在这些参数之间过渡动画
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

ValueAnimator.ofFloat(0f, 1f);怎么做到平滑过渡的
系统 内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:
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);
    }
}
fraction这个值是哪里来的呢?
Interpolator  补间器
控制动画的变化速率,系统默认的Interpolator其实就是一个先加速后减速的Interpolator,对应的实现类就是AccelerateDecelerateInterpolator
AccelerateInterpolator 加速补间器
DecelerateInterpolator 减速补间器
LinearInterpolator 匀速补间器

TimeInterpolator接口的getInterpolation方法返回的就是fraction值,参数input范围在0-1之间,0表示动画开始,1表示结束
/**
 * 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值是匀速增加的,因而fraction的值也是匀速增加的,所以动画的运动情况也是匀速的
AccelerateDecelerateInterpolator
    public float getInterpolation(float input) {  
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;  
    }
getInterpolation()方法最终返回的结果值还是在0到1之间。只不过经过了余弦运算之后,最终的结果不再是匀速增加的了,而是经历了一个先加速后减速的过程

所以如果自己要定义动画的变化速率,必须自己实现一个补间器,实现TimeInterpolator接口的getInterpolation方法。这也是难点

如果需要对任意一个类类型对象而不是int,float型进行动画操作,那么必须自定义一个类并TypeEvaluator接口的evaluate方法。

然后添加AnimatorUpdateListener监听器,通过getAnimatedValue就可以得到evaluate计算出来的结果

public class PointEvaluator implements TypeEvaluator{

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
        Point point = new Point(x, y);
        return point;
    }

}

ValueAnimator.ofObject(new PointEvaluator(), point1, point2);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
            @Override  
            public void onAnimationUpdate(ValueAnimator animation) {  
                currentPoint = (Point) animation.getAnimatedValue();  
                invalidate();  
            }  
        }); 

start():启动动画
setDuration():设置动画持续时间
setStartDelay():设置动画延迟播放的时间,
setRepeatCount():设置动画循环播放的次数,-1表示无限循环
setRepeatMode():设置循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思
setInterpolator():设置补间器,常见的有LinearInterpolator

ObjectAnimator

使用简介

继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的。

ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
public static ObjectAnimator ofInt(Object target, String propertyName, int... values)
public static ObjectAnimator ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
类似ValueAnimator的ofObject,也必须自定义一个类实现TypeEvaluator接口的evaluate方法。只是属性必须提供get,set方法,同时不需要手动把值给取出来

ObjectAnimator anim = ObjectAnimator.ofObject(myAnimView, "color", new ColorEvaluator(),
    "#0000FF", "#FF0000");
anim.setDuration(5000);
anim.start();

使用举例

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  
animator.setDuration(5000);  
animator.start()

这里的propertyName可以是 alpha-透明度,scaleY-缩放Y方向,translationX-平移X方向,rotation-旋转等只要有对应的get,set方法都可以。当然自定义view的时候也可以
注意这里的translationX是相对getLeft来的,所以一开始是0

组合动画

实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)
将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
    after(Animator anim)   将现有动画插入到传入的动画之后执行
    after(long delay)   将现有动画延迟指定毫秒后执行
    before(Animator anim)   将现有动画插入到传入的动画之前执行

    with(Animator anim)   将现有动画和传入的动画同时执行

playTogether 调用这个方法,也可以同时进行动画。。   

Animator监听器

1. anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()...)
动画执行过程中不断执行,可以获取得到当前动画执行的值

2. anim.addListener(new AnimatorListener()....)
onAnimationStart():会在动画开始的时候调用
onAnimationRepeat():会在动画重复执行的时候调用
onAnimationEnd():会在动画结束的时候调用
onAnimationCancel():会在动画被取消的时候调用

AnimatorListenerAdapter实现了AnimatorListener接口,可以选择实现上述方法

使用XML编写动画

    <animator>  对应代码中的ValueAnimator
    <objectAnimator>  对应代码中的ObjectAnimator
    <set>  对应代码中的AnimatorSet
复杂的组合动画操作,比如将一个视图先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially" >

    <objectAnimator
        android:duration="2000"
        android:propertyName="translationX"
        android:valueFrom="-500"
        android:valueTo="0"
        android:valueType="floatType" >
    </objectAnimator>

    <set android:ordering="together" >
        <objectAnimator
            android:duration="3000"
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType" >
        </objectAnimator>

        <set android:ordering="sequentially" >
            <objectAnimator
                android:duration="1500"
                android:propertyName="alpha"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType" >
            </objectAnimator>
            <objectAnimator
                android:duration="1500"
                android:propertyName="alpha"
                android:valueFrom="0"
                android:valueTo="1"
                android:valueType="floatType" >
            </objectAnimator>
        </set>
    </set>

</set>

 

ViewPropertyAnimator


ViewPropertyAnimator提供了更加易懂、更加面向对象的API

textview.animate().alpha(0f);

参考

Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法


参考

1. Android属性动画完全解析(上),初识属性动画的基本用法
2. Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法 
3. Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法


注意事项

属性动画只能在api 11以上使用,所以如果项目支持11以下,那么就可以使用NineOldAndroids这个兼容库。。所以以后能不用补间动画就不要去使用补间动画了。。。

NineOldAndroids是github上的一个开源项目,其作用是为了在低版本android上(API11以下)使用属性动画。它的原理其实也很简单,主要就是判断当前sdk版本,如果大于API11,那么就调用官方的API,否则自己实现动画效果。另外,在API使用方面,它与官方的属性动画基本一致


JakeWharton/NineOldAndroids


补间动画

XML配置文件中
alpha 渐变透明度动画效果
scale 渐变尺寸伸缩动画效果
translate 画面转换位置移动动画效果
rotate 画面转移旋转动画效果

Java Code代码中
AlphaAnimation 渐变透明度动画效果
ScaleAnimation 渐变尺寸伸缩动画效果
TranslateAnimation 画面转换位置移动动画效果
RotateAnimation 画面转移旋转动画效果

共同属性

fillAfter  当设置为true ,该动画转化在动画结束后被应用
interpolator 指定一个动画的插入器
duration  属性为动画持续时间
startOffset  表示动画在几秒开始执行

Alpha

fromAlpha 属性为动画起始时透明度
toAlpha   属性为动画结束时透明度
说明: 0.0表示完全透明 1.0表示完全不透明

Scale

fromXScale 属性为动画起始时 X坐标上的伸缩尺寸    
toXScale   属性为动画结束时 X坐标上的伸缩尺寸     
fromYScale 属性为动画起始时Y坐标上的伸缩尺寸    
toYScale   属性为动画结束时Y坐标上的伸缩尺寸
pivotX     属性为动画相对于物件的X坐标的开始位置
pivotY     属性为动画相对于物件的Y坐标的开始位置
说明: 以上两个属性值 从0%-100%中取值,50%为物件的X或Y方向坐标上的中点位置

Translate

fromXDelta 属性为动画起始时 X坐标上的位置    
toXDelta   属性为动画结束时 X坐标上的位置
fromYDelta 属性为动画起始时 Y坐标上的位置
toYDelta   属性为动画结束时 Y坐标上的位置

Rotate

fromDegrees 属性为动画起始时物件的角度    
toDegrees   属性为动画结束时物件旋转的角度 可以大于360度
pivotX     属性为动画相对于物件的X坐标的开始位置
pivotY     属性为动画相对于物件的Y坐标的开始位置


旋转,缩放,平移动画中

默认值是ABSOLUTE,绝对值

    /**
     * The specified dimension is an absolute number of pixels.
     */
    public static final int ABSOLUTE = 0;

    /**
     * The specified dimension holds a float and should be multiplied by the
     * height or width of the object being animated.
     */
    public static final int RELATIVE_TO_SELF = 1;

    /**
     * The specified dimension holds a float and should be multiplied by the
     * height or width of the parent of the object being animated.
     */
    public static final int RELATIVE_TO_PARENT = 2;

使用

Animation myAnimation= AnimationUtils.loadAnimation(this, R.anim.my_action);
AnimationSet是Animation子类

AnimationSet animationSet = new AnimationSet(true);//true表示共享Interpolator
animationSet.setInterpolator(new LinearInterpolator());
animationSet.addAnimation(myAnimation);
animationSet.setFillAfter(true);//最后停到动画结束的状态,默认为false

view.startAnimation(myAnimation);//xml中定义的动画,既可以是单个动画,也可以是通过set控制的组合动画
view.startAnimation(animationSet);//代码中自定义的组合动画


 

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/linear_interpolator"
     android:fillAfter="true" 
     android:shareInterpolator="true">
    <alpha
        android:duration="1000"
        android:fromAlpha="1"
        android:startOffset="1000"
        android:toAlpha="0.5"/>
    <translate
        android:duration="1000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:startOffset="2000"
        android:toXDelta="100"
        android:toYDelta="0"/>
    <scale
        android:duration="1000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="3000"
        android:toXScale="2"
        android:toYScale="2"/>
</set>

 表示,共享线性插值器,均匀速度变化,同时最后view停留到动画结束的状态。1s后执行alpha从完全不透明到完全透明,再过1s执行translate动画,移动到100,0位置,相对自身来说。再然后1s后以view的中心点进行缩放到2倍。


参考

1. http://www.360doc.com/content/13/0102/22/6541311_257754535.shtml

2. http://www.open-open.com/lib/view/open1335777066015.html


帧动画

参考    Android 用Animation-list实现逐帧动画


animation1.xml 放到drawable文件夹下

<?xml version="1.0" encoding="utf-8"?>  
<!--根标签为animation-list,其中oneshot代表着是否只展示一遍,设置为false会不停的循环播放动画  
    根标签下,通过item标签对动画中的每一个图片进行声明  
    android:duration 表示展示所用的该图片的时间长度-->  
<animation-list  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  android:oneshot="true">  
    <item android:drawable="@drawable/icon1" android:duration="150"></item>  
    <item android:drawable="@drawable/icon2" android:duration="150"></item>  
    <item android:drawable="@drawable/icon3" android:duration="150"></item>  
    <item android:drawable="@drawable/icon4" android:duration="150"></item>  
    <item android:drawable="@drawable/icon5" android:duration="150"></item>  
    <item android:drawable="@drawable/icon6" android:duration="150"></item>  
</animation-list>

使用

启动帧动画

animationIV.setImageResource(R.drawable.animation1);
AnimationDrawable animationDrawable = (AnimationDrawable) animationIV.getDrawable();
animationDrawable.start(); 

停止帧动画

AnimationDrawable animationDrawable = (AnimationDrawable) animationIV.getDrawable();
animationDrawable.stop();




 

帧动画原理上只是不停的绘制背景而已所以不存在补间动画,点击然后又回到原来位置的类似情况。。。。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值