Android动画实现详解

源码传送门

前言

我们都知道,漂亮的用户界面是衡量一款应用”好坏”很重要的依据,因为人都是视觉动物,就好比说花容月貌总有男人为之倾倒,英俊潇洒总能博得芳心。这是一个不容置疑的事实,那么我们的应用也是如此,一个漂亮的用户交互界面能提升用户对应用的好感,提升用户体验。而动画是提升用户体验的一个重要因素,好的动画交互让人用着更舒心,那么今天的这篇文章就是介绍Android中动画实现,让我们的应用动起来。

Android动画分类

在Android中我们一般将动画分为两类,一类是View Animation(视图动画),一类是Property Animation,当然也有说分为三种,Frame Animation,Tween Animation,和Property Animation,由于前两种一般都是作用于整个View的,所以就统称为View Animation。在谷歌官方文档中也是按两种分类来讲解的。

在Android 5.0开始增加了Material Design ,Material Design 中实现了一些动画为用户提供操作反馈并在用户与您的应用进行互动时提供视觉连续性。 它将为按钮与操作行为转换提供一些默认动画,我们可以定制触摸反馈,使用揭露效果,定制操作行为转换,指定定制转换,使用转换启动一个操作行为,以共享元素启动一个操作行为等等。

Frame Animation

由于Frame Animation,Tween Animation的实现还是有区别的,暂且就将这两种方式实现分开介绍,Frame Animation其实就是将一系列的图片一张一张的展示出来,这个和我们看电影是类似的,由于人眼有一个可接收的暂留时间,这也就是为什么人在线看视频会感觉卡顿。当然我们实现Frame Animation就是这个依据,当播放相同的图片张数用时越短也就越流畅,自然人就会感觉是一个动画。我们常见的Gif其实也是帧动画,如果你用PhotoShop打开Gif动画,会发现里面是有很多图层的也就是很多帧。

对于Frame动画有两种实现方式,第一种方式就是在drawable文件夹下创建一个xml文件。它的语法很简单,如下

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot=["true" | "false"] >
    <item
        android:drawable="@[package:]drawable/drawable_resource_name"
        android:duration="integer" />
</animation-list>

看了上面你会发现实现Frame动画很简单,属性很少,animation-list 是动画的根元素,在根元素中的oneshot属性表示动画执行次数,如果设置为true表示只播放一次,如果false则表示会一直循环执行。在根元素下有item元素,该元素就是我们要添加的图片,每一个item表示一帧,item下的drawable就是我们的图片资源,duration就是该帧动画执行的时间。例如

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@mipmap/run1"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run2"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run3"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run4"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run5"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run6"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run7"
        android:duration="200" />
    <item
        android:drawable="@mipmap/run8"
        android:duration="200" />
</animation-list>

使用方法如下

//可以在xml中设置background   
     imageView.setBackgroundResource(R.drawable.frame_run_animation);
AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getBackground();
animationDrawable.start();

frame.gif

运行效果图如上,在上面我们没有添加oneshot属性,则该属性默认false,也就是说该动画会一直循环执行,当我们设置true后则播放到最后一帧时动画停止,当我们想停止时可以使用AnimationDrawable 的stop方法,它还提供了isRunning()方法判断是否已经在执行动画。另外我们还需要注意的是小心OOM。

当然用代码实现也很简单,如下

    private void addFrame() {
        AnimationDrawable animationDrawable = new AnimationDrawable();
        int[] mipmaps = new int[]{R.mipmap.run1, R.mipmap.run2, R.mipmap.run3,
                R.mipmap.run4, R.mipmap.run5, R.mipmap.run6, R.mipmap.run7, R.mipmap.run8,};
        for (int i = 1; i <= 8; i++) {
            int id=mipmaps[i-1];
            //或者使用下面方式,注意如果图片资源放在drawable下时将mipmap修改下
            //int id = getResources().getIdentifier("run" + i, "mipmap", getPackageName());
            Drawable drawable = getResources().getDrawable(id);
            animationDrawable.addFrame(drawable, 200);
        }
        animationDrawable.setOneShot(false);
        imageView.setBackgroundDrawable(animationDrawable);
        animationDrawable.start();
    }

Tween Animation

Tween Animation即补间动画,主要分为四种,分别是平移、缩放、旋转、透明度,直接上语法

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    //插值器
    android:interpolator="@[package:]anim/interpolator_resource"
    //动画结束后View是否停留在结束的位置
    android:fillAfter=["true" | "false"] 
    //重复的模式,默认为restart,即重头开始重新运行,reverse即从结束开始向前重新运行
   android:repeatMode="restart/reverse"
    //子元素是否共用此插值器
    android:shareInterpolator=["true" | "false"] >
    <alpha
        //开始透明度0.0(全透明)到1.0(完全不透明)
        android:fromAlpha="float"
        android:toAlpha="float"
        //动画执行时间
        android:duration="integer" />
    <scale 
        //其实X缩放大于0,1的时候表示不缩放,小于1缩小,大于1放大
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
       //缩放中心,也可以穿fraction值
        android:pivotX="float"
        android:pivotY="float"
        android:duration="integer" />
    <translate
        //表示 x 的起始值
        android:fromXDelta="float/fraction"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" 
        android:duration="integer" />
    <rotate
        //起始的旋转角度
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float"
        android:duration="integer"  />
    <set>
        ...
    </set>
</set>

这是官方给的语法,set 是一个动画集合,内部可以是多个动画的组合,也可以嵌套set,这里包含了动画实现的所有属性。在上面的语法中我们需要注意的是平移的时候其实位置接受百分比数值:从-100到100的值,以“%”结尾,表示百分比相对于自身;从-100到100的值,以“%p”结尾,表示百分比相对于父容器。例如平移开始位置在自身中间则是50%,如平时开始位置在父容器的则是50%p.

例如有些人给我们的Activity会加一些从左边进右边出的动画,那么当我们打开Activity时将Activity布局的fromXDelta值-100%p并将toXDelta为0%p,那么我们看到的效果就是从左边进入了。

插值器

在动画插值器起的作用主要是改变动画的执行速率,一般情况我们不需要自己实现插值器,因为在Android中已经给我们提供了9种插值器,应该够我们使用了,我们使用插值器后会让动画执行的效果更酷炫,当然想自定义插值器也不难,可以查看已经实现插值器源码做参考。

  • accelerate_decelerate_interpolator:先加速后减速
  • accelerate_interpolator:一直加速
  • anticipate_interpolator: 开始的时候先向后甩一点然后向前,就好比人扔东西会先向后甩一下,这样才能抛的远
  • anticipate_overshoot_interpolator:和上一种插值器相似,先向后抛然后向前,到达终点后会有回弹一下效果,好比我们将球抛到墙上,然后反弹回来
  • bounce_interpolator:动画结束的时候弹起,类似皮球落地,会弹几下才停止
  • cycle_interpolator:动画循环播放特定的次数回到原点,速率改变沿着正弦曲线
  • decelerate_interpolator:减速的插值器,刚开始速度快,然后越来越慢直到停止
  • linear_interpolator:线性的插值器。从开始到结束匀速运动
  • overshoot_interpolator:向前超过设定值一点然后返回

下面简单实现一个动画,动画效果如下面截图,是一个透明度,平移,缩放的动画同时执行的动画。

frame1.gif

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/bounce_interpolator"
    android:shareInterpolator="true">
    <translate
        android:duration="400"
        android:fromXDelta="50%p"
        android:fromYDelta="-50%p"
        android:toXDelta="0%"
        android:toYDelta="0%" />
    <scale
        android:duration="400"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:toXScale="1.0"
        android:toYScale="1.0" />
    <alpha
        android:duration="400"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

然后使用下面代码给ImageView加入动画。

 Animation animation= AnimationUtils.loadAnimation(this,R.anim.anim_in_left_top);
  imageView.startAnimation(animation);

当然我们也可给动画加上监听。如

animation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                    }
                    @Override
                    public void onAnimationEnd(Animation animation) {
                    }
                    @Override
                    public void onAnimationRepeat(Animation animation) {
                    }
                });

上面的监听分别是动画开始结束和更新时候的回调。我们在回调中做一些额外的操作。在代码中实现动画就不在细说,主要对应AnimationSetTranslateAnimationScaleAnimationAlphaAnimationRotateAnimation

Propterty Animation

属性动画是3.0之后引入的,在View动画中虽然我们看到了我们的控件位置发生发生变化,比如Button虽然位置变化了,但是点击响应区域还在原来的位置。而属性动画就可以解决这种问题。它可以作用于View的属性。
语法

<set
//执行的顺序together同时执行,sequentially连续执行
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
      //属性动画名字,例如alpha" 或者"backgroundColor"等
        android:propertyName="string"
        android:duration="int"
        //属性的开始值
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        //该动画开始的延迟时间
        android:startOffset="int"
        //动画重复次数,-1表示一直循环,1表示循环一次也就是播放两次,默认0,播放一次
        android:repeatCount="int"
        //repeatCount设置-1时才会有效果
        android:repeatMode=["repeat" | "reverse"]
        //如果值是颜色,则不用使用此属性
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <set>
        ...
    </set>
</set>

下面列出了常见的属性名字,另外需要注意的是,使用属性动画时,必须有相应属性的set/get方法,否则属性动画没有效果的。

  • translationX 和 translationY : 控制View距离左边和顶部的距离的增加值。是一个相对值。相对于自身位置的具体。
  • rotation 、 rotationX 和 rotationY : rotation 是控制View围绕其支点进行旋转。 rotationX 和 rotationY 分别是围绕X轴和Y轴旋转。

  • scaleX 和 scaleY : 控制View的缩放。

  • pivotX 和 pivotY : 控制View的支点位置,进行旋转和缩放,默认是View的中点。它们都是 float 值, 0 表示View的最左边和最顶端, 1 表示最右端和最下端。

  • alpha : 控制View的透明度。

  • x 和 y : 控制View在布局容器中距离左边和顶部的距离。是一个绝对值。

例如我们实现一个旋转加透明度变化的动画,效果图如下
frame1.gif

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="500"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="180" />
    <objectAnimator
        android:duration="500"
        android:propertyName="alpha"
        android:valueFrom="1.0f"
        android:valueTo="0.5f" />
</set>

然后

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this,
                R.animator.property_animator);
set.setTarget(imageView);
set.start();

当然不用xml书写也是很简单的,如下代码

      ObjectAnimator.ofFloat(imageView,"rotation",0,180,90,180)
                .setDuration(2000).start();

代码实现的效果就是在2秒内先旋转到180度,在回到90度在转回180度
效果图如

frame1.gif

在上面代码实现了一直属性动画,那么如果我们想同时作用几个属性那该如何操作呢。此时我们有两种实现方式分别是类PropertyValuesHolder和AnimatorSet,话不多说,先上图再直接上代码。

frame1.gif

    private void startPropertyAnimation3(){
        PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", -200,-100,100, 200,300);
        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.0f);
        PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
        PropertyValuesHolder rotationY = PropertyValuesHolder.ofFloat("rotationX", 0f, 180f);
        ObjectAnimator together = ObjectAnimator.ofPropertyValuesHolder(imageView, translationX,rotate, scaleX, rotationY);
        together.setDuration(3000);
        together.start();
    }
//或者使用AnimatorSet,此方法使用的是按顺序播放。
    private void startPropertyAnimation4(){
        ObjectAnimator translationX = ObjectAnimator.ofFloat(imageView, "translationX", -200,-100,100, 200,300);
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(imageView, "scaleX", 1.0f, 2.0f).setDuration(1000);
        ObjectAnimator rotation = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f).setDuration(1000);
        ObjectAnimator rotationX=ObjectAnimator.ofFloat(imageView,"rotationX", 0f, 180f).setDuration(1000);
        AnimatorSet set = new AnimatorSet();
        set.playSequentially(translationX, scaleX, rotation,rotationX);
        set.setDuration(4000);
        set.start();
    }

Fragment/Activity动画

其实实现Activity及Fragment切换动画也是很简单的,具体的动画效果制作可以使用即使上面介绍的补间动画。例如我们Fragment动画。

FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
transaction.replace(R.id.fragment_container, fragment, fragmentTag);
transaction.commit();

Activity实现动画也很简单,在Activity中提供了overridePendingTransition(int enterAnim, int exitAnim) 方法,该方法接收两个参数,第一个参数是Activity进入时的动画,第二个参数是Activity退出时的动画。该方法一般写在startActivity()后和finish()后,如果我们想打开或者退出不显示动画,可将参数设置为0。

在上面的我们介绍了Activity/Fragment在代码中实现动画的方法,当然还有一种简单的实现方式,那就是在主题中设置动画。
对于Activity

    <style name="CustomeActivityStyle" parent="AppTheme">
        <item name="android:windowAnimationStyle">@style/AnimationStyle</item>
    </style>
    <style name="AnimationStyle">
        <item name="android:activityCloseEnterAnimation">@anim/slide_in_left</item>
        <item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
        <item name="android:activityOpenEnterAnimation">@anim/slide_in_left</item>
        <item name="android:activityOpenExitAnimation">@anim/slide_out_right</item>
    </style>

可能你不太理解为什么是设置了四种,假如有Activity1和Activity2。当我们在Activity1中跳转到Activity2时,Activity1在页面上消失是执行的:activityOpenExitAnimation动画,Activity2出现在屏幕上执行的动画是activityOpenEnterAnimation。当Activity2 finish返回Activity1时,Activity2执行的动画是activityCloseExitAnimation,Activity1显示在屏幕执行的是activityCloseEnterAnimation。创建上面主题后我们需要将该主题应用到我们的Activty中就可以了。

同理Fragment也可相应设置,如activityCloseEnterAnimation改为fragmentCloseEnterAnimation即可。
除此之外我们也可以使用windowAnimation,它包括 windowEnterAnimation 和 windowExitAnimation。注意的一点就是WindowAnimation的控制权大于Activity/Fragment Animation的控制权。

除了上面介绍的动画实现,还有一些动画是从Android5.0增加的,你可以参考文末给出的链接文章,酷炫的Activity切换动画,打造更好的用户体验。个人感觉这篇文章介绍的挺详细。对本文缺少的内容也做了一个补充。到这里该篇文章就暂时告一段落,有问题欢迎指出,Have a wonderful day.

定义定制动画
酷炫的Activity切换动画,打造更好的用户体验

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值