AndroidAndroid开发那些事Android

Android动画总结(属性动画,补间动画,帧动画)

2016-10-09  本文已影响1572人  DakerYi

Android 动画总结

动画分类

Android中动画大概分为3类:

  1. TweenAnimation(补间动画)
  1. FrameAnimation(帧动画)
  2. PropertyAnimation(属性动画)

PropertyAnimation属性动画

先来看属性动画,这是潮流,也是google推荐使用的。

属性动画和一般的View动画的重要区别在于:

  1. 属性动画控制的是实实在在的属性,但是View动画只是产生一个动画,并没有改变控件的属性。
  2. View动画只能控制View的属性,属性动画可以控制所有属性,只要这个属性有get/set 方法,是什么意思呢?

例如 我们查看ImageView的源码,可以看到它有一个私有属性叫 alpha,表示透明度,同时有 setAlpha()/getAlpha() 方法,所有我们就可以通过属性动画来修改ImageView的Alpha的值来完成动画。

那么问题来了,到底动画是怎么产生的呢?简单的说就是,在动画执行前你需要为动画设置初始值结束值动画时间(这是3个最基本的值),那么ValueAnimator类就可以通过这3个值计算出一串连续的数字,表示动画的过程。
例如:alpha值 from 0 to 255,如果时间设置成 255秒,那么 ValueAnimator产生的值将为每秒变化1,0,1,2,3,4,5,6...,然后将这些值通过 setAlpha() 设置给ImageView,就完成了动画。 当然这个例子比较奇葩,但是我觉得比较好理解。

那么怎么来实践我说的呢?我们来看一个例子:

ValueAnimator

这是ValueAnimator的初级用法,通过 ofFloat() 方法设置起始x坐标起始x+100起始x坐标,就是一个在x轴上一个来回100px的动画。区间是1000ms,注意为了将动画与控件相关联(动画都是需要应用到控件上),需要添加一个 AnimatorUpdateListener,这个回调就是用来产生一系列的中间值,然后我们在回调中将中间值设置给我们的ImageView,那么动画就完成了。

最后我们还是用Log将中间值都打印了出来,就更容易理解ValueAnimator就是用来产生一个值变化的序列的作用。当然这里使用的是默认的线性插值器,变化率是均匀的,如果使用其他插值器,还可以产生不均匀的效果。

float fromX = ivLogo.getTranslationX();
ValueAnimator va = ValueAnimator.ofFloat(fromX, fromX + 100f, fromX);
va.setDuration(1000);

va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float v = (float) animation.getAnimatedValue();
        ivLogo.setTranslationX(v);
        Log.d(TAG, "v:" + v);
    }
});
va.start();

效果图:

test1.gif

打印出来的Log:

D/ANIMATION: v:0.0
D/ANIMATION: v:0.14258027
D/ANIMATION: v:0.53691864
D/ANIMATION: v:1.2311637
D/ANIMATION: v:2.2070706
D/ANIMATION: v:3.3803642
D/ANIMATION: v:4.894352
...

D/ANIMATION: v:84.35657
D/ANIMATION: v:89.65131
D/ANIMATION: v:94.66184
D/ANIMATION: v:100.0
D/ANIMATION: v:94.66184
D/ANIMATION: v:89.651306
D/ANIMATION: v:84.35657
...
D/ANIMATION: v:6.679535
D/ANIMATION: v:4.894348
D/ANIMATION: v:3.380371
D/ANIMATION: v:2.2070618
D/ANIMATION: v:1.2311707
D/ANIMATION: v:0.53691864
D/ANIMATION: v:0.14257813
D/ANIMATION: v:0.0

理解了这个,我们就可以来看看下一个更加使用的类

ObjectAnimator

public final class ObjectAnimator extends ValueAnimator{...}

查看源码就知道,ObjectAnimator是继承于ValueAnimator的,ObjectAnimator使用起来更加简单,因为ValueAnimator还需要我们自己去回调,ObjectAnimator不需要写回调,只需要把要设置的属性,控件,区间等内容告诉它,它自己帮我们完成动画。

例子:

float fromX = ivLogo.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(ivLogo, "translationX", fromX, fromX + 100f, fromX);
oa.setDuration(300);
oa.start();

oa.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationStart(Animator animation) {
        super.onAnimationStart(animation);
    }
});

效果和使用 ValueAnimator 是一样的,所以就不贴图了。刚刚我们说不需要回调,但是这里又写了一个回调的Listener是什么意思呢?大家大可把这个删掉,我这里加上主要是为了说明如果想监听 动画结束,动画开始... 等中间过程,ObjectAnimator也可以做到。只需要添加这个 AnimatorListenerAdapter 抽象类。至于这里为什么是抽象类,大家打开源码就可以明白,为了不实现回调的所有方法,特别加了一个抽象类实现 AnimatorListener 接口,这样就可以想覆盖什么方法就覆盖什么方法,不需要全部都覆盖。这也可以理解为 设计模式中的适配器模式吧,原接口不符合我的要求,我需要一个适配器来完成转换

AnimatorSet

如果上面两个都理解了,那么这个就好理解了。AnimatorSet顾名思义就是动画的集合,我们要将几个属性动画放在一起运行,或者按照序列执行,或者按任意顺序执行都可以实现,看看例子:

ObjectAnimator oaScaleX = ObjectAnimator.ofFloat(ivLogo, "scaleX", 0, 1);
ObjectAnimator oaScaleY = ObjectAnimator.ofFloat(ivLogo, "scaleY", 0, 1);
ObjectAnimator oaRotation = ObjectAnimator.ofFloat(ivLogo, "rotation", 0, 360);
ObjectAnimator oaAlpha = ObjectAnimator.ofFloat(ivLogo, "alpha", 1, 0);


AnimatorSet as = new AnimatorSet();
as.play(oaScaleX).with(oaScaleY).with(oaRotation);
as.play(oaAlpha).after(oaRotation);
as.setDuration(1000);
as.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        ivLogo.setAlpha(1.0f);
        Log.d(TAG, "finish:" + ivLogo.getAlpha());
    }
});
as.start();

首先构造了4个 ObjectAnimator对象,分别是沿X轴变形,沿Y轴变形,旋转,透明度,然后我们让前3个一起执行,之后在执行渐变,最后添加一个动画结束回调,将Alpha设置为1,表示不透明,否则我们的控件就看不见了。

效果图:

test2.gif

使用xml文件配置属性动画

android的套路,一定会允许你使用xml的方式来配置动画,所以我们看看怎么配置:

ba358c4d-541e-4396-bd29-135a3be2142b.png

res 文件夹下新建文件夹 animator, 然后创建一个 动画xml文件:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">

    <objectAnimator
        android:duration="2000"
        android:propertyName="x"
        android:valueFrom="0"
        android:valueTo="200"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="2000"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="360"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="2000"
        android:propertyName="x"
        android:valueFrom="200"
        android:valueTo="400"
        android:valueType="floatType" />

</set>

android:ordering="sequentially"表示依次执行,

android:propertyName="rotation"表示修改的属性值

android:valueType="floatType"表示属性值类型

其他的都好理解了。

然后在代码中使用:

Animator animator = AnimatorInflater.loadAnimator(PropertyActivity.this, R.animator.animator_1);
animator.setTarget(ivLogo);
animator.start();

效果图:

test3.gif

使用插值器

我们知道 ValueAnimator就是用来产生一些动画属性中间值,但是默认是均匀变化的,插值器就是要使得产生的中间序列非均匀化,当然不仅是非均匀这么简单,还有很多特效:
下面是官网提供的一些插值器,大家都可以试试,我这里只是抛砖引玉,展示最简单的用法:

d6691284-7830-44e4-8abd-0ad2fc047b6a.png

使用方法:

float fromX = ivLogo.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(ivLogo, "translationX", fromX, fromX + 100, fromX);
oa.setInterpolator(new BounceInterpolator());
oa.setDuration(1000);
oa.start();

只比前面多了一行:oa.setInterpolator(new BounceInterpolator());

效果图:

test4.gif

TweenAnimation补间动画

如果上面的属性动画你都理解了,那么补间动画就更好理解了:

通过代码,和xml配置创建Animation

btnAnimation.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Animation animation = new TranslateAnimation(0, 200, 0, 200);
        animation.setDuration(1000);
        animation.setFillAfter(true);
        ivLogo.startAnimation(animation);
    }
});

btnXml.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Animation animation = AnimationUtils.loadAnimation(TweenActivity.this, R.anim.anim1);
        animation.setDuration(1000);
        animation.setFillAfter(true);
        ivLogo.startAnimation(animation);
    }
});

ivLogo.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d(TAG, "onClick");
    }
});

anim1.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator">

    <translate
        android:fromXDelta="200"
        android:fromYDelta="200"
        android:toXDelta="0"
        android:toYDelta="0" />

</set>

代码很简单,看一下就明白了,这里给 ivLogo添加了一个onclick事件,是为了说明tween动画修改的不是view的真实属性,怎么说呢?如果点击第一个动画,ivLogo的位置相对于原来的位置已经偏离到 (+200, +200) 的位置,但是这个时候如果依然点击 ivLog原位置,依然会打印 "onClick", 所以其实View的位置属性是没有变化的。所以Tween动画修改的不是属性值,而只是产生的一个动画效果而已。

效果图:

test5.gif

学习Animation的话,关注比较多的应该是Animation的一些属性设置,大家感兴趣可以参考官网。

FrameAnimation帧动画

上面两个动画都与控件相关,但是帧动画就完全是图片的叠加。原理和 gif 动画 或者电影一样,是因为每一帧过的速度太快,眼睛来不及反应所以我们觉得是动画。

anim_frame.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item
        android:drawable="@drawable/logo1"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo2"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo3"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo4"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo5"
        android:duration="100" />
    <item
        android:drawable="@drawable/logo6"
        android:duration="100" />
</animation-list>
  1. 根元素为 animation-list
  2. android:oneshot="false" 表示动画一直循环执行

代码调用:

btnStart.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ivWifi.setImageResource(R.drawable.anim_frame);
        AnimationDrawable animationDrawable = (AnimationDrawable) ivWifi.getDrawable();

        if (isRun) {
            animationDrawable.stop();
        } else {
            animationDrawable.start();
        }
        isRun = !isRun;
    }
});

一个全局变量表示动画是否在执行,然后点击按钮时更换状态。

效果图:

test6.gif

总结

例子代码下载链接:
http://download.csdn.net/detail/u013647382/9648454

Android中提供了3种动画,以后的趋势还是属性动画,所以大家不要犹豫,快来钻研属性动画吧!

上一篇下一篇

猜你喜欢

热点阅读