天啦噜!原来Android补间动画可以这么玩

提起动画,无论是哪种语言哪种系统框架,比如说android、iOS、H5、Flash等,动画在其之中都扮演着举足轻重的角色。Android系统中最常用的动画方式有三种:

  • 补间动画(Tween Animation)
  • 帧动画(Frame Animation)
  • 属性动画(Property Animation)

本文就总结一下补间动画的相关玩法。

Android动画系列:

什么是补间动画

Creates an animation by performing a series of transformations on a single image with an Animation.

Tween Animation,通过 Animation 对象在图像上执行一系列的变换而形成的动画。举例来说,就是 Tween Animation 可以改变界面上显示控件的状态,如 Button 的显示、隐藏,ImageView 的尺寸缩放等等。

补间动画分类

补间动画包括五类动画,分别是:

  • AlphaAnimation,主要用于控制 View 的可见性(显示|隐藏)。
  • ScaleAnimation,主要用于缩放 View 大小。
  • TranslateAnimation,主要用于移动 View 的位置。
  • RotateAnimation,主要用于旋转 View。
  • AnimationSet,某些场景仅靠上面单一类型的动画是无法实现的,需要多个类型的动画组合才能达到最终的效果,AnimationSet 的主要作用就是组合各类 Tween Animation。

补间动画的实现形式

补间动画的实现形式有两种:xml创建和code实现。其中xml创建的xml动画文件要放在res/anim/目录下。

  • res/anim/
    目录下放的是视图动画的XML实现和布局动画(LayoutAnimation)
  • res/animations/
    目录下存放的是属性动画的XML实现
  • res/drawable/
    目录下存放的是帧动画的XML实现

AlphaAnimation

通过XML创建

语法

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="integer"
    android:fillAfter="true|false"
    android:fillBefore="true|false"
    android:fillEnabled="true|false"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:repeatCount="infinite|integer"
    android:repeatMode="reverse|restart"
    android:fromAlpha="float"
    android:toAlpha="float" />

AlphaAnimation属性详解

示例

XML定义:

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@integer/integer_one_thousand_and_two_hundred"
    android:fillAfter="true"
    android:fromAlpha="@integer/integer_one"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toAlpha="@integer/integer_zero" />

代码调用:

Button mButton = (Button) findViewById(R.id.Button);
// 创建动画对象,并传入设置的动画效果xml文件
Animation alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_alpha);
// 播放动画
mButton.startAnimation(alphaAnimation);

其他动画XML定义形式在代码调用这部分也是类似。

通过代码实现

语法

AlphaAnimation alphaAnimation = new AlphaAnimation(float fromAlpha, float toAlpha);
alphaAnimation.setInterpolator(Interpolator i);
alphaAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(alphaAnimation);

示例

AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.1f);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
alphaAnimation.setFillAfter(mIsSaveAnimationState);
alphaAnimation.setDuration(800);
mTarget.startAnimation(alphaAnimation);

ScaleAnimation

通过XML创建

语法

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="integer"
    android:fillAfter="true|false"
    android:fillBefore="true|false"
    android:fillEnabled="true|false"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:repeatCount="infinite|integer"
    android:repeatMode="reverse|restart"
    android:fromXScale="float"
    android:fromYScale="float" 
    android:toXScale="float"
    android:toYScale="float" 
    android:pivotX="float"
    android:pivotY="float"
    />

ScaleAnimation属性详解

示例

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@integer/integer_one_thousand_and_two_hundred"
    android:fillAfter="true"
    android:fromXScale="@fraction/percent_one_hundred"
    android:fromYScale="@fraction/percent_one_hundred"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:pivotX="@fraction/percent_fifty"
    android:pivotY="@fraction/percent_fifty"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toXScale="@fraction/percent_two_hundred"
    android:toYScale="@fraction/percent_two_hundred" />

通过代码实现

语法

ScaleAnimation scaleAnimation = new ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue);
scaleAnimation.setInterpolator(Interpolator i);
scaleAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(scaleAnimation); 

示例

ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 2f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setInterpolator(new AccelerateInterpolator());
scaleAnimation.setFillAfter(mIsSaveAnimationState);
scaleAnimation.setDuration(800);
mTarget.startAnimation(scaleAnimation);

TranslateAnimation

通过XML创建

语法

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="integer"
    android:fillAfter="true|false"
    android:fillBefore="true|false"
    android:fillEnabled="true|false"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:repeatCount="infinite|integer"
    android:repeatMode="reverse|restart"
    android:fromXDelta="float"
    android:fromYDelta="float" 
    android:toXDelta="float"
    android:toYDelta="float" 
    />

TranslateAnimation属性详解

示例

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1200"
    android:fillAfter="true"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:interpolator="@anim/overshoot_interpolator"
    android:toXDelta="0"
    android:toYDelta="50%p" />

通过代码实现

语法

TranslateAnimation translateAnimation = new TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta);
translateAnimation.setInterpolator(Interpolator i);
translateAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(translateAnimation); 

示例

TranslateAnimation translateAnimation = new TranslateAnimation(0f, 200f, 0f, 200f);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(mIsSaveAnimationState);
translateAnimation.setDuration(800);
mTarget.startAnimation(translateAnimation);

RotateAnimation

通过XML创建

语法

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="integer"
    android:fillAfter="true|false"
    android:fillBefore="true|false"
    android:fillEnabled="true|false"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:repeatCount="infinite|integer"
    android:repeatMode="reverse|restart"
    android:fromDegrees="float"
    android:toDegrees="float" 
    android:pivotX="float"
    android:pivotY="float" 
    />

RotateAnimation属性详解

示例

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1200"
    android:fillAfter="true"
    android:fromDegrees="0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:toDegrees="360" />

通过代码实现

语法

RotateAnimation rotateAnimation = new RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue);
rotateAnimation.setInterpolator(Interpolator i);
rotateAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(rotateAnimation); 

示例

RotateAnimation rotateAnimation = new RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue);
rotateAnimation.setInterpolator(Interpolator i);
rotateAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(rotateAnimation); 

AnimationSet

通过XML创建

语法

<?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>

AnimationSet属性详解

示例

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@integer/integer_three_thousand"
    android:fillAfter="true"
    android:shareInterpolator="true">
    <translate
        android:fromXDelta="@integer/integer_zero"
        android:fromYDelta="@integer/integer_zero"
        android:toXDelta="@integer/integer_zero"
        android:toYDelta="@integer/integer_two_hundred" />
    <alpha
        android:fromAlpha="@integer/integer_one"
        android:toAlpha="@fraction/scale_smaller" />
    <rotate
        android:fromDegrees="@integer/integer_zero"
        android:pivotX="@fraction/percent_fifty"
        android:pivotY="@fraction/percent_fifty"
        android:toDegrees="@integer/integer_seven_hundred_and_five" />
</set>

通过代码实现

语法

AnimationSet animationSet = new AnimationSet(boolean shareInterpolator);
animationSet.addAnimation(Animation a)
...
AnimationTarget.startAnimation(animationSet);

示例

AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
alphaAnimation.setFillAfter(mIsSaveAnimationState);
alphaAnimation.setDuration(800);

ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 2f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setInterpolator(new AccelerateInterpolator());
scaleAnimation.setFillAfter(mIsSaveAnimationState);
scaleAnimation.setDuration(800);

RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setInterpolator(new AccelerateInterpolator());
rotateAnimation.setFillAfter(mIsSaveAnimationState);
rotateAnimation.setDuration(800);

AnimationSet animationSet = new AnimationSet(false);
animationSet.setFillAfter(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(rotateAnimation);
mTarget.startAnimation(animationSet);

动画监听

Animation类通过监听动画开始 / 结束 / 重复时刻可以进行一系列自定义操作,如跳转页面等等。通过在 Java 代码里setAnimationListener()方法设置:

      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个方法,有些时候我们只需要实现其中一个,因此其余的很累赘,我们可以采用动画适配器AnimatorListenerAdapter来针对化的实现:

// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
anim.addListener(new AnimatorListenerAdapter() {
    
      
    @Override  
    public void onAnimationStart(Animator animation) {
    
      
    // 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
    }  
});  

插值器

细心的同学观察到以上动画属性中几乎都存在一个插值器属性。插值器是动画执行速率调节器,主要用来控制动画的变化率。

常用的插值器汇总

常用的插值器汇总

自定义插值器

自定义插值器同样可以有两种方式:XML和CODE。

通过XML自定义

通过 XML 自定义插值器的时候,限制性比较大,因为系统只提供了部分插值器的自定义,如 AccelerateInterpolator,有些插值器是不支持自定义的,如 AccelerateDecelerateInterpolator。

我们看看AccelerateInterpolator如何自定义,AccelerateInterpolator中可以自定义的属性只有:android:factor,表示加速的比率(The acceleration rate),默认值为 1。

<!-- custom accelerateInterpolator --> 
<?xml version="1.0" encoding="utf-8"?>
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:factor="4.0" />


<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1200"
    android:fillAfter="true"
    android:fillBefore="false"
    android:fillEnabled="false"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:interpolator="@android:anim/custom_accelerate_interpolator"
    android:toXDelta="40%p"
    android:toYDelta="0" />

可以通过 XML 自定义插值器,除了 AccelerateInterpolator,还有很多,以下是具体列表:

可通过XML自定义的插值器

通过代码自定义

通过 CODE 自定义插值器就没有那么多限制, 也很简单,只要实现 Interpolator 接口,并实现其中的方法(getInterpolation)就好了。接下来,我们先看下 Google 官方是如何实现插值器的。

//AccelerateDecelerateInterpolator

package android.view.animation;

import android.content.Context;
import android.util.AttributeSet;

import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;

/**
 * An interpolator where the rate of change starts and ends slowly but
 * accelerates through the middle.
 */
@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();
    }
}

通过代码可知,AccelerateDecelerateInterpolator 是通过余弦函数实现的。在 AccelerateDecelerateInterpolator 中,加速的过程是函数曲线斜率逐渐增大的过程,减速的过程是函数曲线斜率逐渐减小的过程。

AccelerateDecelerateInterpolator插值器原理图

明白了AccelerateDecelerateInterpolator插值器原理之后,我们用正切函数实现一个 DecelerateAccelerateInterpolator。(快-慢-快)

自定义DecelerateAccelerateInterpolator插值器

1.Interpolator 接口中 getInterpolation 方法中 input 的取值范围为 [0,1],而蓝色框圈出的 X 的取值范围为 [-π/4,π/4],所以,需要将 [0,1] 转换为 [-π/4,π/4]:

π/2 * input - π/4

2.正切函数在 [-π/4,π/4] 取值范围内,相应的函数值的取值范围为[-1,1],而 getInterpolation 最终返回值的取值范围为 [0,1],所以,需要将 [-1,1] 转换为 [0,1]:

(tan(π/2 * input - π/4) + 1)/2

所以代码实现过程:

//自定义
public class DecelerateAccelerateInterpolator implements Interpolator {
    
    

    @Override
    public float getInterpolation(float input) {
    
    
        return (float) ((Math.tan(Math.PI/2 * input - Math.PI/4) + 1)/2);
    }
}

//调用
TranslateAnimation translateAnimation = new TranslateAnimation(0f, 0f, 0f, 800f);
translateAnimation.setInterpolator(new DecelerateAccelerateInterpolator());
translateAnimation.setFillAfter(mIsSaveAnimationState);
translateAnimation.setDuration(1800);
mTarget.startAnimation(translateAnimation);

使用场景

补间动画常用于视图View的一些标准动画效果:平移、旋转、缩放,透明度等,除了常规的动画使用,补间动画还有一些特殊的应用场景,例如Activity和Fragment的切换动效以及视图组(ViewGroup)中子元素的出场效果。

Activity 的切换效果

页面进入动画:

Intent intent = new Intent (this,Acvtivity.class);
startActivity(intent);
// enter_anim(b页面的进场动画),exit_anim(a页面的消失动画)
// 特别注意:overridePendingTransition()必须要在startActivity(intent)后被调用才能生效
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);

页面退出动画:

@Override
public void finish(){
    
    
    super.finish();
    // 特别注意: overridePendingTransition()必须要在finish()后被调用才能生效
    overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}

系统自带的效果android.R.anim.xxx

// 淡入淡出的动画效果      
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);

// 从左向右滑动的效果
overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);

Fragment 切换效果

系统自带的切换效果:

FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
// 通过setTransition(int transit)进行设置
// transit参数说明
// 1. FragmentTransaction.TRANSIT_NONE:无动画
// 2. FragmentTransaction.TRANSIT_FRAGMENT_OPEN:标准的打开动画效果
// 3. FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:标准的关闭动画效果
// 标准动画设置好后,在Fragment添加和移除的时候都会有。
fragmentTransaction.setTransition(int transit);

自定义动画效果:

FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
// 采用`FragmentTransavtion`的 `setCustomAnimations()`进行设置
fragmentTransaction.setCustomAnimations(R.anim.in_from_right,R.anim.out_to_left);

视图组(ViewGroup)中子元素的出场效果

有些时候我们想为ViewGroup中的子元素的出场统一动画规则,那么我们可以这样做:

1.定义子元素统一出场动画:

<?xml version="1.0" encoding="utf-8"?>
// 此处采用了组合动画
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    android:duration="3000"

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

    <translate
        android:fromXDelta="500"
        android:toXDelta="0"
         />
</set>

2.定义视图组(ViewGroup)动画管理规则:

<?xml version="1.0" encoding="utf-8"?>
// 采用LayoutAnimation标签
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    // 子元素开始动画的时间延迟
    // 如子元素入场动画的时间总长设置为300ms
    // 那么 delay = "0.5" 表示每个子元素都会延迟150ms才会播放动画效果
    // 第一个子元素延迟150ms播放入场效果;第二个延迟300ms,以此类推
    android:delay="0.5"
   
    // 表示子元素动画的顺序
    // 可设置属性为:
    // 1. normal :顺序显示,即排在前面的子元素先播放入场动画
    // 2. reverse:倒序显示,即排在后面的子元素先播放入场动画
    // 3. random:随机播放入场动画
    android:animationOrder="normal"
    
    // 设置入场的具体动画效果
    // 将步骤1的子元素出场动画设置到这里
    android:animation="@anim/view_animation"
    />

3.为视图组(ViewGroup)指定andorid:layoutAnimation属性,这里有两种方式:xml设置和code设置。

方式1:在 XML 中指定:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical" >
    <ListView
        android:id="@+id/listView1"
        android:layoutAnimation="@anim/anim_layout"
        // 指定layoutAnimation属性用以指定子元素的入场动画
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

方式2:在Java代码中指定【这样就不用额外设置res/ anim /anim_layout.xml该xml文件了】:

ListView lv = (ListView) findViewById(R.id.listView1);
// 加载子元素的出场动画
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_item);
// 设置LayoutAnimation的属性
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
// 为ListView设置LayoutAnimation的属性
lv.setLayoutAnimation(controller);

参考

  • https://developer.android.google.cn/guide/topics/resources/animation-resource#Tween
  • https://juejin.cn/post/6844903793713233927#heading-64
  • https://blog.csdn.net/carson_ho/article/details/72827747

猜你喜欢

转载自blog.csdn.net/li0978/article/details/114199346