Android 视图动画(View Animation) 使用详解

谨以文章记录学习历程,如有错误还请指明。

动画综述

Google大大对动画的总述如下:

Animations can add visual cues that notify users about what’s going on in your app. They are especially useful when the UI changes state, such as when new content loads or new actions become available. Animations also add a polished look to your app, which gives it a higher quality look and feel.

没错,放上原文我只是装个逼,~
简单来说,动画就两个作用:

  • 添加可视提示,通知我们这个APP中正在发生的事情。比如用户界面发生变化时,有新的内容加载或某些操作变为可用。
  • 提供高逼格的外观(装逼利器

动画的分类如下:
动画分类

视图动画(View Animation)

补间动画(Tween Animation)

概述

  • 作用于视图对象View,如TextView,不可作用于属性,如点击响应位置等
  • 通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画
  • 动画可以在视图对象的内容上执行一系列简单的转换(位置、大小、旋转和透明度)。如果你有一个TextView对象,你可以移动,旋转,缩放文本。如果它有一个背景图像,背景图像将随着文本一起被转换。
  • XMLJAVA代码定义。建议使用XML文件,因为它比更可读、可重用和可切换

分类

根据动画效果,补间动画分为以下4类
* 透明度动画(alpha)
* 缩放动画(scale)
* 平移动画(Translate)
* 旋转动画(rotate)

不同动画与Java类、xml文件关键字对应关系如下:

名称 Java子类 xml关键字 说明
透明度动画 AlphaAnimation <alpha> 放置在res/anim/目录下 透明度渐变
旋转动画 RotateAnimation <rotate> 放置在res/anim/目录下 视图旋转
缩放动画 ScaleAnimation <scale> 放置在res/anim/目录下 放大/缩小 视图尺寸大小
平移动画 TranslateAnimation <translate> 放置在res/anim/目录下 视图位置移动
复合动画 AnimationSet <set> 放置在res/anim/目录下 一个持有其它动画元素alpha、scale、translate、rotate或者其它set元素的容器

详细说明

补间动画的继承关系:
继承关系

由于Animation是抽象基类,其提供了一些通用的动画属性方法,如下所示

xml属性 Java方法 说明
android:detachWallpaper setDetachWallpaper(boolean) 是否在壁纸上运行
android:duration setDuration(long) 动画的运行时间(以毫秒为单位);必须设置
android:fillAfter setFillAfter(boolean) 动画结束时是否保持动画最后的状态;默认为false,优先于fillBefore
android:fillBefore setFillBefore(boolean) 动画结束时是否还原到开始动画前的状态;默认为true
android:fillEnabled setFillEnabled(boolean) 是否应用fillBefore的值,对fillAfter无影响;默认为true
android:interpolator setInterpolator(Interpolator) 设定插值器(指定的动画效果,譬如回弹等)
android:repeatCount setRepeatCount(int) 重复次数
android:repeatMode setRepeatMode(int) 重复类型有两个值,reverse表示倒序回放,restart表示从头播放
android:startOffset setStartOffset(long) 调用start函数之后等待开始运行的时间,单位为毫秒
android:zAdjustment setZAdjustment(int) 表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal


透明度动画(Alpha)– 属性
xml属性 Java方法 说明
android:fromAlpha AlphaAnimation(float fromAlpha, …) 动画开始的透明度(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlpha AlphaAnimation(…, float toAlpha) 动画结束的透明度,同上


旋转动画(Rotate)– 属性
xml属性 Java方法 说明
android:fromDegrees RotateAnimation(float fromDegrees, …) 旋转开始角度,正代表顺时针度数,负代表逆时针度数
android:toDegrees RotateAnimation(…, float toDegrees, …) 旋转结束角度,正代表顺时针度数,负代表逆时针度数
android:pivotX RotateAnimation(…, float pivotX, …) 缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:pivotY RotateAnimation(…, float pivotY) 缩放起点Y坐标,同上规律


缩放动画(Scale)– 属性
xml属性 Java方法 说明
android:fromXScale ScaleAnimation(float fromX, …) 初始X轴缩放比例,1.0表示无变化
android:toXScale ScaleAnimation(…, float toX, …) 结束X轴缩放比例
android:fromYScale ScaleAnimation(…, float fromY, …) 初始Y轴缩放比例
android:toYScale ScaleAnimation(…, float toY, …) 结束Y轴缩放比例
android:pivotX ScaleAnimation(…, float pivotX, …) 缩放起点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:pivotY ScaleAnimation(…, float pivotY) 缩放起点Y轴坐标,同上规律


平移动画(Translate)– 属性
xml属性 Java方法 说明
android:fromXDelta TranslateAnimation(float fromXDelta, …) 起始点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:fromYDelta TranslateAnimation(…, float fromYDelta, …) 起始点Y轴从标,同上规律
android:toXDelta TranslateAnimation(…, float toXDelta, …) 结束点X轴坐标,同上规律
android:toYDelta TranslateAnimation(…, float toYDelta) 结束点Y轴坐标,同上规律


具体使用

下面我们直接以复合动画为例,演示如何通过XML以及JAVA两种方式使用动画

XML方式

  • 文件位置:
    res/anim/filename.xml
  • 资源引用
    Java: R.anim.filename
    XML: @[package:]anim/filename
  • 语法:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator=["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
    <set>
        ...
    </set>
</set>
  • 创建动画
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation myAnimation= AnimationUtils.loadAnimation(this, R.anim.filename);
spaceshipImage.startAnimation(myAnimation);

以上就是一个标准的XML方式使用自定义的补间动画的模板

JAVA方式

示例如下:

ImageView imageView = findViewById(R.id.image_view);
        // 创建 需要设置动画的 视图View

        // 组合动画设置
        AnimationSet setAnimation = new AnimationSet(true);
        // 创建组合动画对象(设置为true)

        // 设置组合动画的属性
        setAnimation.setRepeatMode(Animation.RESTART);
        ...

        // 逐个创建子动画,不作过多描述

        // 子动画1:透明度动画
        Animation alpha = new AlphaAnimation(1,0);
        alpha.setDuration(3000);
        ...

        // 子动画2:缩放动画
        Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
        scale1.setDuration(1000);
        ...

        // 子动画3:平移动画
        Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
                TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
                TranslateAnimation.RELATIVE_TO_SELF,0
                ,TranslateAnimation.RELATIVE_TO_SELF,0);
        translate.setDuration(10000);
        ...

        // 子动画4:旋转动画
        Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
        rotate.setDuration(1000);
        rotate.setRepeatMode(Animation.RESTART);
        rotate.setRepeatCount(Animation.INFINITE);
        ...


        // 将创建的子动画添加到组合动画里
        setAnimation.addAnimation(alpha);
        setAnimation.addAnimation(rotate);
        setAnimation.addAnimation(translate);
        setAnimation.addAnimation(scale1);

        imageView.startAnimation(setAnimation);
        // 播放动画

首选XML方式使用动画(不意味着XML方式全面优于JAVA方式)

  • XML方式:可读性好
  • JAVA方式:可动态创建动画效果

至于补间动画的使用,Animation还有如下一些比较实用的方法介绍:

Animation类的方法 解释
reset() 重置Animation的初始化
cancel() 取消Animation动画
start() 开始Animation动画
hasStarted() 判断当前Animation是否开始
hasEnded() 判断当前Animation是否结束

既然补间动画只能给View使用,那就来看看View中和动画相关的几个常用方法吧,如下:

View类的常用动画操作方法 解释
startAnimation(Animation animation) 对当前View开始设置的Animation动画
clearAnimation() 取消当View在执行的Animation动画

特别特别注意:补间动画执行之后并未改变View的真实布局属性值。切记这一点,譬如我们在Activity中有一个Button在屏幕上方,我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方,这时如果点击屏幕下方动画执行之后的Button是没有任何反应的,而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件。

动画监听

  • 动画监听器接收来自动画的通知。通知表示与动画相关的事件,例如动画的结束或重复
  • 使用方法:
Animation.addListener(new AnimatorListener() {
          @Override
          public void onAnimationStart(Animation animation) {
              //动画开始时执行
          }

           @Override
          public void onAnimationRepeat(Animation animation) {
              //动画重复时执行
          }

         @Override
          public void onAnimationCancel()(Animation animation) {
              //动画取消时执行
          }

          @Override
          public void onAnimationEnd(Animation animation) {
              //动画结束时执行
          }
      });
  • 补间动画的监听只能通过此方式,且必须要复写全部4个方法。
  • 属性动画时,可以采用Animator.addListener(new AnimatorListenerAdapter(){
    //复写指定方法
    })
    ——动画适配器AnimatorListenerAdapter中已经实现好每个接口

插值器Interpolator

简介

插值器是在XML中定义的一个动画修改器,它影响动画的变化率。这允许现有动画附加加速、减速、重复、反弹等效果。

继承关系

系统为我们提供了上图中的各种插值器,其都是实现了Interpolator接口的实现类,具体说明如下:

java类 XML 资源ID 说明
AccelerateDecelerateInterpolator @android:anim/accelerate_decelerate_interpolator 先加速再减速
AccelerateInterpolator @android:anim/accelerate_interpolator 持续加速
AnticipateInterpolator @android:anim/anticipate_interpolator 先退后再加速前进
AnticipateOvershootInterpolator @android:anim/anticipate_overshoot_interpolator 先退后再加速前进,超出终点后再回终点
BounceInterpolator @android:anim/bounce_interpolator 结束时弹球效果
CycleInterpolator @android:anim/cycle_interpolator 周期运动
DecelerateInterpolator @android:anim/decelerate_interpolator 减速
LinearInterpolator @android:anim/linear_interpolator 匀速
OvershootInterpolator @android:anim/overshoot_interpolator 向前弹出一定值之后回到原来位置(快速完成动画,超出再回到结束样式)

使用方法

  • XML方式
<set android:interpolator="@android:anim/accelerate_interpolator">
    ...
</set>
  • JAVA方式
Animation alphaAnimation = new AlphaAnimation(1,0);
                alphaAnimation.setDuration(3000);
                //创建插值器对象
                Interpolator interpolator = new OvershootInterpolator();
                //为动画添加插值器
                alphaAnimation.setInterpolator(interpolator);
                imageView.startAnimation(alphaAnimation);

自定义插值器

某些情景可能你会发现系统提供的插值器不能满足需求,此时我们需要自定义插值器。有两种实现方式:XML自定义实现JAVA代码实现方式

XML实现
  1. res/anim/下创建filename.xml

  2. 修改插值器属性(如下不作任何修改,则与系统预设插值器功能相同)

    <?xml version="1.0" encoding="utf-8"?>
    <InterpolatorName xmlns:android="http://schemas.android.com/apk/res/android"
      android:attribute_name="value"
      />
  3. 补间动画中引用该文件(资源引用为@[package:]anim/filename

XML实现方式本质就是修改系统提供的插值器的某些属性,具体可修改属性如下:

插值器 可修改属性 属性说明
无属性
android:factor Float,加速速率(默认值为1)
android:tension Float. 起始点后拉的张力数(默认值为2)
android:tension
android:extraTension
Float. 起始点后拉的张力数(默认值为2)
Float. 拉力的倍数(默认值为1.5)
无属性
android:cycles Integer. 循环次数(默认为1)
android:factor Float. 减速的速率(默认为1)
无属性
android:tension Float. 超出终点后的张力(默认为2)
JAVA方式

在此只讨论原理,不设计具体逻辑(奈何我是个数学渣,公式真的是一头雾水= =)

在前面继承关系的图中,我们可以看出所有插值器继承自BaseInterpolator,其又实现了Interpolator接口,而Interpolator接口继承自TimeInterpolator接口,TimeInterpolator接口中的唯一抽象方法为getInterpolation(float input),这个方法是由系统调用的,其中的参数input代表动画的时间,在0和1之间,也就是开始和结束之间。

我们可以看一下系统提供的插值器,如AccelerateDecelerateInterpolator,其源码极其极其简单,如下:

@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
    }
}

可以看到,核心方法就是前面提到的getInterpolation(),其效果就是通过这一数学公式得来的,好久不看数学,已经快看不懂这个公式结果是什么了,汗颜= =

public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

由此我们总结以下JAVA方式自定义插值器:

  • 根据需求实现TimeInterpolator接口
  • 实现抽象方法getInterpolation(),在该方法中处理逻辑
  • 为动画设置该 自定义插值器

帧动画(Frame Animation)

概述

  • 作用于视图对象View,如TextView,不可作用于属性,如点击响应位置等
  • 将动画拆分为帧的形式,且每一帧都是一张图片,本质就是按序播放一组预先定义好的图片
  • 两种实现方式:XML & JAVA,依旧推荐XML方式(你问我为什么还是推荐XML?连Google API中都只介绍XML方式,还不能说明问题么)

继承关系


我们很清楚的看到,其本质是Drawable,,因此帧动画的XML定义文件放在res/drawable/目录下

使用说明

XML方式

1.在res/drawable/下创建filename.xml文件
2.设置图片资源,示例如下:

<?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" />
    //如下
    <item android:drawable="@drawable/wheel0" android:duration="50" />
    <item android:drawable="@drawable/wheel1" android:duration="50" />
    <item android:drawable="@drawable/wheel2" android:duration="50" />
    <item android:drawable="@drawable/wheel3" android:duration="50" />
    <item android:drawable="@drawable/wheel4" android:duration="50" />
    <item android:drawable="@drawable/wheel5" android:duration="50" />
</animation-list>

3.启动动画

ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);

rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
rocketAnimation.start();
参数说明

<animation-list>:必须是根节点,包含一个或者多个<item>元素,包含属性如下:

  • android:oneshottrue代表只执行一次,false循环执行。
  • <item> 类似一帧的动画资源。

<item>animation-list的子项,包含属性如下:

  • android:drawable 一帧的Drawable资源。
  • android:duration 一帧显示多长时间。

JAVA方式

<-- 直接从drawable文件夹获取动画资源(图片) -->
        animationDrawable = new AnimationDrawable();
        for (int i = 0; i <= 25; i++) {
            int id = getResources().getIdentifier("a" + i, "drawable", getPackageName());
            Drawable drawable = getResources().getDrawable(id);
            animationDrawable.addFrame(drawable, 100);
        }

        <-- 开始动画 -->
        btn_startFrame.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animationDrawable.setOneShot(true);
                iv.setImageDrawable(animationDrawable);
                // 获取资源对象
                animationDrawable.stop();
                 // 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
                animationDrawable.start();
                // 启动动画

            }
        });

         <-- 停止动画 -->
        btn_stopFrame.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animationDrawable.setOneShot(true);
                iv.setImageDrawable(animationDrawable);
                animationDrawable.stop();
            }
        });

特别注意,AnimationDrawablestart()方法不能在ActivityonCreate()方法中调运,因为AnimationDrawable还未完全附着到window上,所以最好的调运时机是onWindowFocusChanged()方法中。

相关方法说明

XML属性和Java方法的对应关系如下:

XML属性 Java方法 说明
android:drawable addFrame(Drawable frame, int duration)第一个参数 帧的Drawable的引用
android:duration addFrame(Drawable frame, int duration)第二个参数 显示此帧的时间(以毫秒为单位)
android:oneshot setOneShot(boolean oneShot) 如果true,动画将只运行一次,然后停止。
android:visible setVisible(boolean, boolean). 初始可见状态;默认值为false。

一些常用的方法如下:

方法 说明
start() 从第一帧开始动画
stop() 停止动画,显示当前帧
getFrame() 取得某帧的Drawable
isRunning() 动画是否正在运行

特殊说明

  • 优点:使用简单、方便
  • 缺点尽量避免使用尺寸较大的图片,否则会引起OOM

总结

  • 本文对Android中的视图动画作出了总结
  • 关于另一种动画-属性动画,请移步属性动画(Property Animation) 使用详解
  • 笔者水平有限,如有错漏,欢迎指正。
  • 接下来我也会将所学的知识分享出来,有兴趣可以继续关注whd_Alive的Android开发笔记
  • 不定期分享Android开发相关的技术干货,期待与你的交流,共勉。

猜你喜欢

转载自blog.csdn.net/whdalive/article/details/80262190