Android动画使用之补间动画

4,403 阅读7分钟
1. 前言

动画在安卓中,使用是非常常见的,比如网络请求时的loading,就是通过旋转实现的。 在安卓中,动画分为两大类, 分别是视图动画和属性动画。视图动画又分为帧动画和补间动画。这篇文章主要讲补间动画的使用。三种动画的使用文章地址如下:

  1. 帧动画
  2. 补间动画
  3. 属性动画

2. 介绍

补间动画指的是, 设置好动画的开始属性 和 结束属性。 系统会在我们设置的动画时间内, 从开始时属性过渡到结束时候的属性。举个例子,一个控件向右平移100像素,其实改变的属性就是控件x轴的坐标。系统提供了四种补间动画,分别为:

  • 平移
  • 缩放
  • 旋转
  • 透明度

3. 使用
3.1 平移动画
  1. 在res/anim 目录下新建动画描述文件translate.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"


    android:duration="5000"
    android:startOffset="1000"
    android:fillBefore="true"
    android:fillAfter="false"
    android:fillEnabled="true"
    android:repeatMode="restart"
    android:repeatCount="3"


    android:fromXDelta="0"
    android:toXDelta="500"

    android:fromYDelta="0"
    android:toYDelta="500"
    >

</translate>

解释:
第一部分是动画的公共属性,也就是其他三种动画也有这些属性:

  • duration:动画时间,单位毫秒
  • startOffset:动画延迟执行时间
  • fillBefore: 动画播放完后,视图是否会停留在动画开始的状态,默认为true
  • fillAfter: 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
  • fillEnable: 是否应用fillBefore值,对fillAfter值无影响,默认为true
  • restart:选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
  • repeatCount:重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复 下面的是 平移动画的特有属性
  • fromXDelta:视图在水平方向x 移动的起始值
  • toXDelta:视图在水平方向x 移动的结束值
  • fromYDelta:视图在竖直方向y 移动的起始值
  • toYdelta: 视图在竖直方向y 移动的结束值
  1. 在Java代码中加载动画文件并播放
    Animation animation = AnimationUtils.loadAnimation(this, R.anim.translate);
    iv.startAnimation(animation);
    

至此动画就已经定义完成并可以播放了。 第一步中的xml方式也可以通过java代码来实现,代码方式如下:

Animation translateAnimation = new TranslateAnimation(0,500,0,500);
mation

translateAnimation.setDuration(3000);
// 固定属性的设置都是在其属性前加“set”,如setDuration()
iv.startAnimation(translateAnimation);

解释:

  • new TranslateAnimation():这是平移动画的构造函数,传入xml中平移动画的那几个特有属性。
  • setDuration:通过setXX()方法设置上面xml中的动画公共属性
3.2 缩放动画
  1. 在res/anim 目录下新建动画描述文件scale.xml,内容如下:\
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"

    android:duration="3000"
    android:repeatCount="2"
    android:fillBefore="true"
    android:repeatMode="restart"

    android:fromXScale="0"
    android:toXScale="2"
    android:fromYScale="0"
    android:toYScale="2"
    android:pivotX="50%"
    android:pivotY="50%"
    >
</scale>

解释:

  • fromXScale: 水平方向的起始缩放倍数
  • toXScale: 水平方向的结束缩放倍数
  • fromYScale: 垂直方向起始缩放倍数
  • toYScale: 垂直方向结束缩放倍数
  • pivotX:缩放中心点的x坐标
  • pivotY: 缩放中心点的y坐标
  1. 在Java代码中加载动画文件并播放
Animation animation = AnimationUtils.loadAnimation(this, R.anim.scale);
iv.startAnimation(animation);
3.3 旋转动画
  1. 在res/anim 目录下新建动画描述文件rotate.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:repeatCount="2"
    android:repeatMode="restart"
    android:fillBefore="true"

    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    >

</rotate>

解释:

  • fromDegrees:旋转起始角度
  • toDegrees:旋转结束角度
  • pivotX:旋转中心点x轴坐标
  • pivotY: 旋转中心点y轴坐标
  1. 在Java代码中加载动画文件并播放
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate);
        iv.startAnimation(animation);
3.4 透明动画
  1. 在res/anim 目录下新建动画描述文件alpha.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:startOffset="500"
    android:fillBefore="true"

    android:fromAlpha="1.0"
    android:toAlpha="0.0"
    >
</alpha>

解释:

  • fromAlpha:透明度起始值
  • toAlpha: 透明度结束值
  1. 在Java代码中加载动画文件并播放
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.alpha);
        iv.startAnimation(animation);
3.5 组合动画
  1. 在res/anim 目录下新建动画描述文件combination.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"

    android:duration="3000"
    android:fillBefore="true"
    android:repeatCount="0"
    android:repeatMode="restart"
    android:shareInterpolator="true"
    >

    <translate
        android:fromXDelta="0"
        android:toXDelta="250"
        android:fromYDelta="0"
        android:toYDelta="-250"
        android:duration="3000"
        >
    </translate>


    <rotate
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="3000"
        android:fromDegrees="0"
        android:toDegrees="30"
        >
    </rotate>


    <!--<alpha-->
        <!--android:duration="3000"-->
        <!--android:fromAlpha="1.0"-->
        <!--android:toAlpha="0.5"-->
        <!--/>-->


</set>

解释:

  • set:组合动画的根节点
  • shareInterpolator:表示组合动画中的动画是否和集合共享同一个差值器,如果集合不指定插值器,那么子动画需要单独设置

特别注意:

  • 组合动画播放时是全部动画同时开始,如果想不同动画不同时间开始就要使用android:startOffset属性来延迟单个动画播放时间。
  • 在组合动画里scale缩放动画设置的repeatCount(重复播放)和fillBefore(播放完后,视图是否会停留在动画开始的状态)是无效的。所以如果需要重复播放或者回到原位的话需要在set标签里设置。
  1. 在Java代码中加载动画文件并播放
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.combination);
        iv.startAnimation(animation);

4. 实例

image

  1. xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_water"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:src="@mipmap/water"
        android:layout_centerInParent="true"
        android:layout_above="@id/iv_shumiao"
        android:visibility="gone"
        />

    <ImageView
        android:id="@+id/iv_shumiao"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/shumiao"
        android:layout_centerInParent="true"
        />

    <ImageView
        android:id="@+id/iv_shuihu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/shuihu"
        android:layout_below="@id/iv_shumiao"
        />

</RelativeLayout>

  1. java编写组合动画
package com.domain.study.animation.tween;

import android.os.Bundle;
import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;

import com.domain.study.animation.R;
import com.domain.study.animation.base.BaseActivity;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class TweenCombinationActivity extends BaseActivity {

    @BindView(R.id.iv_shumiao)
    ImageView ivShumiao;
    @BindView(R.id.iv_shuihu)
    ImageView ivShuihu;
    @BindView(R.id.iv_water)
    ImageView ivWater;

    int shuiHuX = 0;
    int shuiHuY = 0;

    int shuMiaoX = 0;
    int shuMiaoY = 0;

    int shuMiaoWidth = 0;
    int shuMiaoHeight = 0;

    int shuiHuWidth = 0;
    int shuiHuHeight = 0;

    int waterHeight = 0;

    @Override
    protected int setLayoutId() {
        return R.layout.activity_combination;
    }

    @Override
    protected void initView() {
        ivShuihu.post(new Runnable() {
            @Override
            public void run() {
                int[] location = new int[2];
                ivShuihu.getLocationOnScreen(location);
                shuiHuX = location[0]; // view距离 屏幕左边的距离(即x轴方向)
                shuiHuY = location[1]; // view距离 屏幕顶边的距离(即y轴方向)
                shuiHuWidth = ivShuihu.getWidth();
                shuiHuHeight = ivShuihu.getHeight();
            }
        });
        ivShumiao.post(new Runnable() {
            @Override
            public void run() {
                int[] location = new int[2];
                ivShumiao.getLocationOnScreen(location);
                shuMiaoX = location[0]; // view距离 屏幕左边的距离(即x轴方向)
                shuMiaoY = location[1]; // view距离 屏幕顶边的距离(即y轴方向)

                shuMiaoWidth=ivShumiao.getWidth(); // 获取宽度
                shuMiaoHeight =ivShumiao.getHeight(); // 获取高度
            }
        });

        ivWater.post(new Runnable() {
            @Override
            public void run() {
                waterHeight = ivWater.getHeight();
            }
        });



    }


    @OnClick(R.id.iv_shuihu)
    public void onViewClicked() {
        start();
    }

    private void start(){

        AnimationSet setAnimation = new AnimationSet(true);

// 子动画1:旋转动画
        Animation rotate = new RotateAnimation(0,10,0.5f,0.5f);
        rotate.setDuration(1000);
        rotate.setStartOffset(3000);

// 子动画2:平移动画
        Animation translate = new TranslateAnimation(
                0,
                shuMiaoX-shuiHuWidth,
                0
                ,-(shuiHuHeight+shuMiaoHeight+waterHeight+100));
        translate.setDuration(3000);


//等待水滴落下动画
        Animation wait = new TranslateAnimation(
                0,
                0,
                0
                ,0);
        wait.setStartOffset(4000);
        wait.setDuration(2000);




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

        ivShuihu.startAnimation(setAnimation);


        rotate.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                ivWater.setVisibility(View.VISIBLE);
                startWaterAnimation();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

    }

    private void startWaterAnimation(){

        AnimationSet animationSet = new AnimationSet(true);

        TranslateAnimation translateAnimation = new TranslateAnimation(0,0,0,50);
        translateAnimation.setDuration(2000);

        AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
        alphaAnimation.setDuration(2000);

        animationSet.addAnimation(translateAnimation);
        animationSet.addAnimation(alphaAnimation);
        ivWater.startAnimation(animationSet);

        animationSet.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                ivWater.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

    }

}

5.总结
  • 补间动画系统提供了四种,分别是:平移、缩放、旋转、透明度。
  • 动画可以通过xml文件来描述,也可以通过java代码来实现。
  • 补间动画只能作用在view上。
  • 补间动画其实没有改变view的真实属性,比如动画后看到的view,是没办法点击的。
  • 可以通过组合动画,把四种动画结合使用。
  • 组合动画启动时,所有的子动画是同时启动的,如果要有先后顺序,可以通过子动画的延时执行来实现。

6. 完整demo地址

7. 参考文章

8. 历史文章目录