动画基础全面总结

动画分类:

逐帧动画(Frame)
补间动画(Tween)
属性动画(Property)(Android 3.0以后引入)


帧动画

定义:由N张静态图片收集起来,然后我们通过控制依次显示这些图片,形成动画。

实现帧动画,需要利用AnimationDrawable

方法1. 编写Drawable,然后代码中调用start()以及stop()开始或停止播放动画;
方法2. 在Java代码中创建逐帧动画,创建AnimationDrawable对象,然后调用addFrame(Drawable frame,int duration)向动画中添加帧,接着调用start()和stop();

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@mipmap/img_miao1"
        android:duration="80" />
    <item
        android:drawable="@mipmap/img_miao2"
        android:duration="80" />
    <item
        android:drawable="@mipmap/img_miao3"
        android:duration="80" />
    ...
</animation-list>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_start;
    private Button btn_stop;
    private ImageView img_show;
    private AnimationDrawable anim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
        anim = (AnimationDrawable) img_show.getBackground();
    }

    private void bindViews() {
        btn_start = (Button) findViewById(R.id.btn_start);
        btn_stop = (Button) findViewById(R.id.btn_stop);
        img_show = (ImageView) findViewById(R.id.img_show);
        btn_start.setOnClickListener(this);
        btn_stop.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start:
                anim.start();
                break;
            case R.id.btn_stop:
                anim.stop();
                break;
        }
    }
}

补间动画

定义:指定动画开始,以及动画结束“关键帧”,而动画变化的“中间帧”则由系统计算并补齐。

实现补间动画:在res目录下新建anim文件夹,右键new-Animation Resource file,新建文件的根目录都是set。
xml中,在其子节点中添加代码。

Andoird所支持的补间动画效果有如下这五种(第五种是前面几种的组合):

  • AlphaAnimation透明度渐变效果,创建时许指定开始以及结束透明度,还有动画的持续时间,透明度的变化范围(0,1),0是完全透明,1是完全不透明;对应<alpha/>标签;

  • ScaleAnimation缩放渐变效果,创建时需指定开始以及结束的缩放比,以及缩放参考点,还有动画的持续时间;对应<scale/>标签;

  • TranslateAnimation位移渐变效果,创建时指定起始以及结束位置,并指定动画的持续时间即可;对应<translate/>标签;

  • RotateAnimation旋转渐变效果,创建时指定动画起始以及结束的旋转角度,以及动画持续时间和旋转的轴心;对应<rotate/>标签;

  • AnimationSet组合渐变,就是前面多种渐变的组合,对应<set/>标签。

1)AlphaAnimation(透明度渐变)

<alpha xmlns:android="http://schemas.android.com/apk/res/android"  
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
    android:fromAlpha="1.0"  
    android:toAlpha="0.1"  
    android:duration="2000"/>  

属性解释:

fromAlpha :起始透明度
toAlpha:结束透明度
透明度的范围为:0-1,完全透明-完全不透明

2)ScaleAnimation(缩放渐变)

<scale xmlns:android="http://schemas.android.com/apk/res/android"  
    android:interpolator="@android:anim/accelerate_interpolator"  
    android:fromXScale="0.2"  
    android:toXScale="1.5"  
    android:fromYScale="0.2"  
    android:toYScale="1.5"  
    android:pivotX="50%"  
    android:pivotY="50%"  
    android:fillAfter="false"
    android:duration="2000"/>  

属性解释:

fromXScale/fromYScale:沿着X轴/Y轴缩放的起始比例;
toXScale/toYScale:沿着X轴/Y轴缩放的结束比例;
以上四种属性值: 0.0表示收缩到没有,1.0表示正常无伸缩,值小于1.0表示收缩,值大于1.0表示放大;值为相应倍数

pivotX/pivotY:缩放的中轴点X/Y坐标,即距离自身左边缘的位置,比如50%就是以图像的中心为中轴点;
以上两个属性值:从0%-100%中取值,50%为物件的X或Y方向坐标上的中点位置

3)TranslateAnimation(位移渐变)

anim_translate.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"  
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
    android:fromXDelta="0"  
    android:toXDelta="320"  
    android:fromYDelta="0"  
    android:toYDelta="0"  
    android:duration="2000"/>   

属性解释:

fromXDelta/fromYDelta:动画起始位置的X/Y坐标;
toXDelta/toYDelta:动画结束位置的X/Y坐标;
pivotX/pivotY:表示旋转动作的参考点,不设置表示默认以自己为参照物;

4)RotateAnimation(旋转渐变)

<rotate xmlns:android="http://schemas.android.com/apk/res/android"  
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
    android:fromDegrees="0"  
    android:toDegrees="360"  
    android:duration="1000"  
    android:repeatCount="1"  
    android:repeatMode="reverse"/>  

属性解释:

fromDegrees/toDegrees:旋转的起始/结束角度,当角度为负数表示逆时针旋转,当角度为正数表示顺时针旋转
repeatCount:旋转的次数,默认值为0,代表一次,假如是其他值,比如3,则旋转4次。另外,值为-1或者infinite时,表示动画永不停止
repeatMode:设置重复模式,默认restart,但只有当repeatCount大于0或者infinite或-1时才有效。还可以设置成reverse,表示偶数次显示动画时会做方向相反的运动!

5)AnimationSet(组合渐变)

<set xmlns:android="http://schemas.android.com/apk/res/android"  
    android:interpolator="@android:anim/decelerate_interpolator"  
    android:shareInterpolator="true" >  

    <scale  
        android:duration="2000"  
        android:fromXScale="0.2"  
        android:fromYScale="0.2"  
        android:pivotX="50%"  
        android:pivotY="50%"  
        android:toXScale="1.5"  
        android:toYScale="1.5" />  

    <rotate  
        android:duration="1000"  
        android:fromDegrees="0"  
        android:repeatCount="1"  
        android:repeatMode="reverse"  
        android:toDegrees="360" />  

    <translate  
        android:duration="2000"  
        android:fromXDelta="0"  
        android:fromYDelta="0"  
        android:toXDelta="320"  
        android:toYDelta="0" />  

    <alpha  
        android:duration="2000"  
        android:fromAlpha="1.0"  
        android:toAlpha="0.1" />  

</set>  

属性解释:

fillBefore/ fillAfter:true/false,表示动画的转化在动画结束后是否应用;
fillBefore是指动画结束时画面停留在第一帧,fillAfter是指动画结束是画面停留在最后一帧。
这2个参数不能在 alpha,scale,translate,rotate节点中设置,在其中设置不起作用;
必须1)在动画xml文件的set节点中设置,或者在Activity中设置setFillAfter()/setFillBefore()。

pivot:这个属性主要是在translate 和 scale 动画中,这两种动画都牵扯到view 的“物理位置“发生变化,所以需要一个参考点。
pivotX和pivotY就共同决定了这个点;它的值可以是float或者是百分比数值。
pivotX取值如下,pivotY取值同理。参考点都50%、50%即View自己的中心点。

pivotX取值 含义
10 参考点距离动画所在view自身左边缘10像素
10% 参考点距离动画所在view自身左边缘 的距离是整个view宽度的10%
10%p 参考点距离动画所在view父控件左边缘的距离是整个view宽度的10%

这5个标签有一个共有的属性android:interpolator,Interpolator用来控制动画的变化速度。
而Android中已经提供了五个可供选择的实现类:

  • LinearInterpolator:动画以均匀的速度改变
  • AccelerateInterpolator:在动画开始的地方改变速度较慢,然后开始加速
  • AccelerateDeceerateInterpolator:在动画开始、结束的地方改变速度较慢,中间时加速
  • CycleInterpolator:动画循环播放特定次数,变化速度按正弦曲线改变:Math.sin(2 * mCycles * Math.PI * input)
  • DecelerateInterpolator:在动画开始的地方改变速度较快,然后开始减速
  • AnticipateInterpolator:反向,先向相反方向改变一段再加速播放
  • AnticipateOvershootInterpolator:开始的时候向后然后向前甩一定值后返回最后的值
  • BounceInterpolator: 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
  • OvershottInterpolator:回弹,最后超出目的值然后缓慢改变到目的值

6)实例

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_alpha"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="透明度渐变" />

    <Button
        android:id="@+id/btn_scale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="缩放渐变" />

    <Button
        android:id="@+id/btn_tran"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="位移渐变" />

    <Button
        android:id="@+id/btn_rotate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="旋转渐变" />

    <Button
        android:id="@+id/btn_set"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="组合渐变" />

    <ImageView
        android:id="@+id/img_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="48dp"
        android:src="@mipmap/img_face" />

</LinearLayout>

MainActivity.Java
调用AnimationUtils.loadAnimation()加载动画,View控件调用startAnimation开启动画。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btn_alpha;
    private Button btn_scale;
    private Button btn_tran;
    private Button btn_rotate;
    private Button btn_set;
    private ImageView img_show;
    private Animation animation = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
    }

    private void bindViews() {
        btn_alpha = (Button) findViewById(R.id.btn_alpha);
        btn_scale = (Button) findViewById(R.id.btn_scale);
        btn_tran = (Button) findViewById(R.id.btn_tran);
        btn_rotate = (Button) findViewById(R.id.btn_rotate);
        btn_set = (Button) findViewById(R.id.btn_set);
        img_show = (ImageView) findViewById(R.id.img_show);

        btn_alpha.setOnClickListener(this);
        btn_scale.setOnClickListener(this);
        btn_tran.setOnClickListener(this);
        btn_rotate.setOnClickListener(this);
        btn_set.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_alpha:
                animation = AnimationUtils.loadAnimation(this,
                        R.anim.anim_alpha);
                img_show.startAnimation(animation);
                break;
            case R.id.btn_scale:
                animation = AnimationUtils.loadAnimation(this,
                        R.anim.anim_scale);
                img_show.startAnimation(animation);
                break;
            case R.id.btn_tran:
                animation = AnimationUtils.loadAnimation(this,
                        R.anim.anim_translate);
                img_show.startAnimation(animation);
                break;
            case R.id.btn_rotate:
                animation = AnimationUtils.loadAnimation(this,
                        R.anim.anim_rotate);
                img_show.startAnimation(animation);
                break;
            case R.id.btn_set:
                animation = AnimationUtils.loadAnimation(this,
                        R.anim.anim_set);
                img_show.startAnimation(animation);
                break;
        }
    }
}

动画状态的监听

调用动画对象的: setAnimationListener(new AnimationListener())方法,重写下面的三个方法:

onAnimationStart():动画开始;
onAnimtaionRepeat():动画重复;
onAnimationEnd():动画结束;

为View动态设置动画效果

静态加载:先调用AnimationUtils.loadAnimation(),然后View控件用startAnimation()开始动画;
动态加载:即直接创建一个动画对象,用Java代码完成设置,再调用startAnimation开启动画。


属性动画

定义:通过不断地对属性值进行操作的机制,并将值赋值到指定对象的指定属性上,从而实现动画效果。

使用属性动画原因:

  • 补间动画只是改变了View的显示效果而已,而不会真正去改变View的属性,即只是单纯的动画效果
    • 视觉上,补间动画的set节点中设置android:fillAfter="true",可以实现动画结束后保留在最后一帧,即应用动画效果的改变;
    • 实际上,补间动画只是将view绘制到指定位置,形成动画,实际上的控件依然停留在原有位置;
  • 补间动画只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,即无扩展性,无法实现其他效果
  • 补间动画是只能够作用在View上的动画,对于自定义View无法添加补间动画

1)ValueAnimator

ValueAnimator负责进行对不断变化的属性值的计算,以实现初始值和结束值之间的动画过渡。
将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,ValueAnimator就会自动完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。

使用流程:

  1. 调用ValueAnimator的ofInt(),ofFloat()或ofObject()静态方法创建ValueAnimator实例;方法参数为多个对应类型的值。
  2. 调用实例的setXxx方法设置动画持续时间,插值方式,重复次数等;
  3. 调用实例的addUpdateListener添加AnimatorUpdateListener监听器,在该监听器中可以获得ValueAnimator计算出来的值,值应用到指定对象上;
  4. 调用实例的start()方法开启动画。

使用范例:

//旋转的同时透明度变化
    private void raAnimator(){
        ValueAnimator rValue = ValueAnimator.ofInt(0, 360);
        rValue.setDuration(1000L);
        rValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int rotateValue = (Integer) animation.getAnimatedValue();
                img_babi.setRotation(rotateValue);
                float fractionValue = animation.getAnimatedFraction();
                img_babi.setAlpha(fractionValue);
            }
        });
        rValue.setInterpolator(new DecelerateInterpolator());
        rValue.start();
    }

2)ObjectAnimator

ObjectAnimator为ValueAnimator的子类,使用方法类似于ValueAnimator。
ObjectAnimator能对任意对象的任意属性进行动画操作的,使用范围更广。

ObjectAnimator使用更为简单,不需要自己写回调。

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
//ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f); 
//ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);  
//ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  
animator.setDuration(5000);  
animator.start(); 

对于ObjectAnimator.ofFloat()方法,第二个参数即对应的ObjectAnimator指定要改变的对象的值,可以为任意的值。
常用的参数项有:alpha、rotation、translationX和scaleY等等。

ObjectAnimator内部的工作机制并不是直接对传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,因此alpha属性所对应的get和set方法应该就是getAlpha()与setAlpha(),这两个方法由View对象提供,同理,其他属性一样。即只要继承自View的类都可以对其进行这些动画操作。

3)AnimatorSet

实现组合动画功能
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例。

AnimatorSet.Builder中包括以下四个方法:

  • after(Animator anim) 将现有动画插入到传入的动画之后执行
  • after(long delay) 将现有动画延迟指定毫秒后执行
  • before(Animator anim) 将现有动画插入到传入的动画之前执行
  • with(Animator anim) 将现有动画和传入的动画同时执行
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f); 
AnimatorSet animSet = new AnimatorSet();  
animSet.play(rotate).with(fadeInOut).after(moveIn);  
animSet.setDuration(5000);  
animSet.start();

4)AnimatorListener

Animator类中提供了addListener()方法,这个方法接收一个AnimatorListener。
实现AnimatorListener监听动画的各种事件。

AnimatorListener中重写下面全部的四个回调方法:

anim.addListener(new AnimatorListener() {  
    //  动画开始
    public void onAnimationStart(Animator animation) {
    }  
  
    //  动画重复执行  
    public void onAnimationRepeat(Animator animation) {  
    }  
  
    //  动画结束
    public void onAnimationEnd(Animator animation) {  
    }  
  
    //  动画取消
    public void onAnimationCancel(Animator animation) {  
    }  
});

当只想重写其中部分方法时,接收的是AnimatorListenerAdapter,解决接口繁琐的问题。

anim.addListener(new AnimatorListenerAdapter() {  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
});  

5)使用xml定义动画

使用标签定义动画,Activity中调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画。
但是xml定义动画效果容易失效!建议使用Java代码方式动态加载动画

在res目录下新建animator文件夹,右键new-Animation Resource file,新建文件的根目录都是setXML中,在其子节点中添加代码。

  • animator :对应代码中的ValueAnimator
  • objectAnimator :对应代码中的ObjectAnimator
  • set :对应代码中的AnimatorSet

定义动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:propertyName="string"
        android:duration="1000"
        android:valueFrom="@android:color/white"
        android:valueTo="@android:color/holo_green_dark"
        android:startOffset="500"
        android:repeatCount="1"
        android:repeatMode="restart"
        android:valueType="intType"/>
    <animator
        android:duration="1000"
        android:valueFrom="@android:color/white"
        android:valueTo="@android:color/holo_green_dark"
        android:startOffset="10"
        android:repeatCount="1"
        android:repeatMode="restart"
        android:valueType="intType"/>
</set>

使用动画

    Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
    animator.setTarget(view);  
    animator.start();  

ValueAnimator、ObjectAnimator高级用法:
http://blog.csdn.net/guolin_blog/article/details/43816093
http://blog.csdn.net/guolin_blog/article/details/44171115


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

推荐阅读更多精彩内容