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

Android 动画总结

动画分类

Android中动画大概分为3类:

  1. TweenAnimation(补间动画)
  • TranslateAnimation
  • ScaleAnimation
  • RotateAnimation
  • AlphaAnimation
  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种动画,以后的趋势还是属性动画,所以大家不要犹豫,快来钻研属性动画吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容