Android动画详解

Android应用中经常需要使用到各式各样的动画效果,产品设计总是已他们重破天机的想象给出看着就很难实现的动画,不过值得庆幸的是Android的动画框架也在不断地进化以满足我们的需求。Android发展到现在,有多少种可以使用的动画类型呢,在本篇文章中将给出一些例子来给出答案。


在Android3.0之前的,我们可以使用的动画类型仅仅有两种,只能满足一些简单的功能效果,他们分别是逐帧动画和补间动画,而在Android3.0时,Android SDK为开发者带来了强大而又灵活的属性动画Property Animation(听上去就好高大上啊),可以称之为动画界的重大突破,它可以只通过简单的api调用就实现较为复杂的动画效果。而随着时间的推进啊,在Android4.4时,又为开发者带来了android.transtion框架(基本常用的动画效果都给你封装好了,你就偷着乐吧),我们通过框架可以更直观的定义动画效果,接下来我们简单介绍一下除属性动画之外的三种动画类型(因为属性动画比较牛逼,一定要重点讲解,目前在一些自定义View中,使用一些动画效果都是使用属性动画)。


装逼ing

1.1 逐帧动画(Frame Animation)
逐帧动画也叫Drawable Animation,是最简单最基本的动画类型了,他就是利用人眼的视觉暂留效应——也就是光对视网膜所产生的视觉在光停止作用后也会保留一段时间的现象。那意思就是说,逐帧动画就是一帧一帧的图像咯!是的没错,设计师给出一系列状态不断变化的图片,开发者可以制定动画中每一帧对于的图片和持续的时间,然后就可以播放帧动画了。下面我们通过xml的代码的形式分别实现类似物流配送时的动画效果:
![image.png](http://upload-images.jianshu.io/upload_images/5815336- 5f61db40219e3da9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

图片资源放一下:


a.png

b.png

c.png

d.png

1.1.1:xml方式实现逐帧动画
这是实现逐帧动画最常用的方式,首先我们将每一帧的图片放入drawable目录下,然后在res/drawable目录中新建一个xml文件,在这里文件中根目录使用<animation-list>标签来定义帧动画执行顺序,在其中不仅可以指定每一帧的图片,还可以指定动画持续时间等。具体如下:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">

<!--oneshot是否重复执行动画,false表示只执行一次,true表示执行
    item帧动画顺序,drawable帧图片,duration动画持续时长-->

<item android:drawable="@drawable/a" android:duration="150"></item>
<item android:drawable="@drawable/b" android:duration="150"></item>
<item android:drawable="@drawable/c" android:duration="150"></item>
<item android:drawable="@drawable/d" android:duration="150"></item>


</animation-list>

之后我们只需要在xml中创建一个ImageView控件,设置其背景为刚才drawable目录下定义的xml文件:
··

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.xjf.androidanimation.MainActivity">

<ImageView
    android:id="@+id/iv"
    android:layout_width="60dp"
    android:layout_height="40dp"
    android:background="@drawable/frame_anim"
    android:layout_gravity="center" />

</LinearLayout>

最后记得在Java中获取到AnimationDrawable对象,调用start方法开始执行动画:

  ImageView iv = findViewById(R.id.iv);
    AnimationDrawable animationDrawable = (AnimationDrawable) iv.getBackground();
    animationDrawable.start();

1.1.2:代码方式:

   ImageView imageView = findViewById(R.id.iv);
    AnimationDrawable animationDrawable = new AnimationDrawable();
    Drawable a = getResources().getDrawable(R.drawable.a);
    Drawable b = getResources().getDrawable(R.drawable.b);
    Drawable c = getResources().getDrawable(R.drawable.c);
    Drawable d = getResources().getDrawable(R.drawable.d);
    animationDrawable.addFrame(a,120);
    animationDrawable.addFrame(b,120);
    animationDrawable.addFrame(c,120);
    animationDrawable.addFrame(d,120);

    imageView.setBackgroundDrawable(animationDrawable);
    animationDrawable.setOneShot(false);
    //动画开始
    animationDrawable.start();
    //停止
    animationDrawable.stop();

1.2:补间动画(Tween Animation)
补间动画不需要用户定义动画啊执行过程的每一帧,只需要定义动画开始和结束这两个关键帧,并指定动画的变化时间和方式即可,之后交给Android系统进行计算,通过在两个帧之间插入渐变值来实现平滑过渡,从而对View的内容完成一系列的图形变换来实现动画效果,它主要包括四种动画:透明度变化Alpha,大小变化Scale,位移变化Translate及旋转变化Rotate,这四种动画也可以组合,从而生成更加炫酷的效果,同样定义补间动画也有XML和代码两种方式可以实现,在了解动画之前,我们有必要搞清楚插值器Interpolator。

1.2.1 插值器Interpolator
上一段落有简单提到Android系统会在补间动画的开始和结束帧直接插入渐变值,它依据的便是Interpolator,具体来说,Interpolator会根据类型的不同,选择不同的算法计算出在补间动画期间所需要动态插入帧的密度和位置,Interpolator负责控制动画的变化速度,使得前面所说的四种基本动画能够以匀速、加速、减速、抛物线等多种效果进行变化。

具体到Android代码中,你可以发现Interpolator类是一个空接口,它继承自TimeInterpolator,TimeInterpolator时间插值器允许动画进行非线性运动变换,如加速或减速等,该接口中世纪中有一个方法,入参是0.0-1.0的一个值,返回值可以小于0也可以大于1。

public interface TimeInterpolator {

/**
 * Maps a value representing the elapsed fraction of an animation to a value that represents
 * the interpolated fraction. This interpolated value is then multiplied by the change in
 * value of an animation to derive the animated value at the current elapsed animation time.
 *
 * @param input A value between 0 and 1.0 indicating our current point
 *        in the animation where 0 represents the start and 1.0 represents
 *        the end
 * @return The interpolation value. This value can be more than 1.0 for
 *         interpolators which overshoot their targets, or less than 0 for
 *         interpolators that undershoot their targets.
 */
float getInterpolation(float input);}

AndroidSDK默认提供了几个Interpolator的实现类来满足我们的基本需求:


image.png

意义如下:

AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后然后向前甩
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置

如果上述的默认插值器不符合我们项目的实际需求,开发者也可以通过实现Interpolator接口来编写自己的插值器。

好了,关于插值器的介绍就到这吧,具体的效果朋友们可以在自己编写的Tween Animation中加入不同的插值器去了解。

1.3 补间动画Tween Animation具体实现:
首先我们看一下,系统提供了哪些:


1-3-1.png

然后了解一下他们各自的效果(其实就是我们上面所讲的四种动画类型了):


1.--2.png

1.3.1:AlphaAnimation(透明度动画效果)

xml方式定义:在res/anim(没有anim就自己新建一个)中新建一个xml文件,根元素为set:


1-3-3
<?xml version="1.0" encoding="utf-8"?>
  <set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
>
<translate
    android:fromYDelta="100%p"
    android:toYDelta="0"></translate>

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

在Java代码中:

  //设置透明度变化为0-1
    AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
    //设置动画时长
    alphaAnimation.setDuration(200);
   //创建插值器
    AccelerateDecelerateInterpolator interpolator = new         AccelerateDecelerateInterpolator();
    //插值器和动画绑定
    alphaAnimation.setInterpolator(interpolator);
    //设置动画结束后保留结束状态
    alphaAnimation.setFillAfter(true);
    imageView.setAnimation(alphaAnimation);

AlphaAnimation动画的构造方法只有一个:(参数分别是初始的透明度和结束的透明度)

 /**
 * Constructor to use when building an AlphaAnimation from code
 * 
 * @param fromAlpha Starting alpha value for the animation, where 1.0 means
 *        fully opaque and 0.0 means fully transparent.
 * @param toAlpha Ending alpha value for the animation.
 */
public AlphaAnimation(float fromAlpha, float toAlpha) {
    mFromAlpha = fromAlpha;
    mToAlpha = toAlpha; }

1.3.2:ScaleAnimation
xml方式:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="200">

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

Java代码:

   ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f,4.0f,1.0f,4.0f, Animation.RELATIVE_TO_SELF,0.0f,Animation.RELATIVE_TO_SELF,0.0f);
    scaleAnimation.setDuration(200);
    scaleAnimation.setFillAfter(true);
    imageView.setAnimation(scaleAnimation);

ScaleAnimation的构造方法就比较多啦:

public ScaleAnimation(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ScaleAnimation(float fromX, float toX, float fromY, float toY) {
  
}

public ScaleAnimation(float fromX, float toX, float fromY, float toY,
        float pivotX, float pivotY) {
   }


public ScaleAnimation(float fromX, float toX, float fromY, float toY,
        int pivotXType, float pivotXValue, int pivotYType, float pivotYValue
}

可以看到入参非常多,我们来看一下各参数的含义:

float fromX           动画起始时 X坐标上的伸缩尺寸   
float toX               动画结束时 X坐标上的伸缩尺寸   
float fromY           动画起始时Y坐标上的伸缩尺寸   
float toY               动画结束时Y坐标上的伸缩尺寸   
int pivotXType      动画在X轴相对于物件位置类型   
float pivotXValue  动画相对于物件的X坐标的开始位置   
int pivotYType      动画在Y轴相对于物件位置类型   
float pivotYValue  动画相对于物件的Y坐标的开始位置 

1.3.3 translateAnimation

xml实现:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="0"
    android:toYDelta="1000"></translate>
</set>

JAVA代码实现:

 TranslateAnimation translateAnimation = new     TranslateAnimation(Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f,Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f);
    scaleAnimation.setDuration(200);
    scaleAnimation.setFillAfter(true);
    imageView.setAnimation(scaleAnimation);

构造方法:

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
}


public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
        int fromYType, float fromYValue, int toYType, float toYValue) {
}

构造方法各参数含义:

参数fromXType:开始时x轴相对于组件的位置类型。
参数fromXValue:开始时x轴的坐标。根据fromXType代表不同的意义。
参数toXType:结束时x轴相对于组件的位置类型。
参数toXValue:结束时x轴的坐标。根据toXType代表不同的意义。
参数fromYType:开始时y轴相对于组件的位置类型。
参数fromYValue:开始时y轴的坐标。根据fromYType代表不同的意义。
参数toYType:结束时y轴相对于组件的位置类型。
参数toYValue:结束时y轴的坐标。根据toYType代表不同的意义。

关于入参时的参数值:


参数值详解.png

1.3.4:RorateAnimation

XML方式:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatMode="restart"
android:startOffset="0">
<rotate
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360"
    ></rotate>
</set>

Java代码实现:

RotateAnimation rotateAnimation = new RotateAnimation(0,-720,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    scaleAnimation.setDuration(1000);
    scaleAnimation.setFillAfter(true);
    imageView.setAnimation(scaleAnimation);

构造方法:

  public RotateAnimation(float fromDegrees, float toDegrees) {
}

public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {

}

public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
        int pivotYType, float pivotYValue) {
}

参数:

fromDegrees:旋转的起始角度 
toDegrees:旋转的终止角度 
pivotX:该对象被旋转的点的X坐标,指定为一个绝对数字,其中0是左边缘。 
pivotY:对象被旋转的点的Y坐标,指定为一个绝对数字,其中0是顶部边缘。

1.3.5自定义补间动画
在实际开发中,可能会遇到四种基本动画无法实现的需求,这时可以自定义补间动画,只需要继承Animation,重写这个抽象基类中的applyTransformation方法:

public class MyAnimtion extends Animation{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    super.applyTransformation(interpolatedTime, t);
}
}

interpolator表示动画的时间进行比,值在0-1变化,第二个参数表示补间动画在不同时刻对View的变形程度.

对于Tween Animation的介绍就到此告一段落,有想看各动画插值器效果和了解具体自定义补间动画的朋友可参考博客:http://blog.csdn.net/u011043551/article/details/65443751

1.4 属性动画(Property Animation)

                                                         这是本篇文章的重点,开发必考题
吸引一些大家的眼光.png
重点.png

属性动画是在Android3.0时引入的,在补间动画中,我们只能改变View的绘制效果,View的真实属性是没有改变的,而属性动画则可以直接改变View对象的属性值,同时属性动画几乎可以对任何对象执行动画,而不是局限在View对象上。

首先简单了解一下相关属性:
Duration动画的持续时间,默认300ms。
Time interpolation:时间差值,乍一看不知道是什么,但是我说LinearInterpolator、AccelerateDecelerateInterpolator,大家一定知道是干嘛的了,定义动画的变化率。
Repeat count and behavior:重复次数、以及重复模式;可以定义重复多少次;重复时从头开始,还是反向。
Animator sets: 动画集合,你可以定义一组动画,一起执行或者顺序执行。
Frame refresh delay:帧刷新延迟,对于你的动画,多久刷新一次帧;默认为10ms,但最终依赖系统的当前状态;基本不用管。

相关的类
ObjectAnimator 动画的执行类,后面详细介绍
ValueAnimator 动画的执行类,后面详细介绍
AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。
AnimatorInflater 用户加载属性动画的xml文件
TypeEvaluator 类型估值,主要用于设置动画操作属性的值。
TimeInterpolator 时间插值,上面已经介绍。
总的来说,属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性。

属性动画的基类是Animator,它是一个抽象类,我们不会直接使用这个类,通常都是继承它并重写其中的相关方法,Android SDK为开发者默认提供了几个子类,大多数情况下使用这些子类就可以完成开发任务了。

1.4.1:Evaluator
在介绍Animator之前,我们先来了解一个名为Evaluator的概念,它是用来控制属性动画如何计算属性值的,接口定义是TypeEvaluator,其中定义了evaluate方法,供不同类型的子类实现。

public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}

而系统也有提供了一些常见的实现类,比如IntEvaluator、FloateEvaluator、ArghEvaluator等。我们来看一下ArgbEvaluator的具体实现,可以看到实现逻辑很简单,就是根据输入的初始值和结束值及一个进度比,计算出每一个进度对于的ARGB值。

public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();

/**
 * Returns an instance of <code>ArgbEvaluator</code> that may be used in
 * {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
 * be used in multiple <code>Animator</code>s because it holds no state.
 * @return An instance of <code>ArgbEvalutor</code>.
 *
 * @hide
 */
public static ArgbEvaluator getInstance() {
    return sInstance;
}

/**
 * This function returns the calculated in-between value for a color
 * given integers that represent the start and end values in the four
 * bytes of the 32-bit int. Each channel is separately linearly interpolated
 * and the resulting calculated values are recombined into the return value.
 *
 * @param fraction The fraction from the starting to the ending values
 * @param startValue A 32-bit int value representing colors in the
 * separate bytes of the parameter
 * @param endValue A 32-bit int value representing colors in the
 * separate bytes of the parameter
 * @return A value that is calculated to be the linearly interpolated
 * result, derived by separating the start and end values into separate
 * color channels and interpolating each one separately, recombining the
 * resulting values in the same way.
 */
public Object evaluate(float fraction, Object startValue, Object endValue) {
    int startInt = (Integer) startValue;
    float startA = ((startInt >> 24) & 0xff) / 255.0f;
    float startR = ((startInt >> 16) & 0xff) / 255.0f;
    float startG = ((startInt >>  8) & 0xff) / 255.0f;
    float startB = ( startInt        & 0xff) / 255.0f;

    int endInt = (Integer) endValue;
    float endA = ((endInt >> 24) & 0xff) / 255.0f;
    float endR = ((endInt >> 16) & 0xff) / 255.0f;
    float endG = ((endInt >>  8) & 0xff) / 255.0f;
    float endB = ( endInt        & 0xff) / 255.0f;

    // convert from sRGB to linear
    startR = (float) Math.pow(startR, 2.2);
    startG = (float) Math.pow(startG, 2.2);
    startB = (float) Math.pow(startB, 2.2);

    endR = (float) Math.pow(endR, 2.2);
    endG = (float) Math.pow(endG, 2.2);
    endB = (float) Math.pow(endB, 2.2);

    // compute the interpolated color in linear space
    float a = startA + fraction * (endA - startA);
    float r = startR + fraction * (endR - startR);
    float g = startG + fraction * (endG - startG);
    float b = startB + fraction * (endB - startB);

    // convert back to sRGB in the [0..255] range
    a = a * 255.0f;
    r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
    g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
    b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

    return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}

1.4.2:AnimatorSet
AnimatorSet是Animator的子类,用来组合多个Animator,并指定这些Animator是顺序播放还是同时播放。

1.4.3:ValueAnimator:
ValueAnimator是属性动画最重要的一个类,继承自Animator,它定义了属性动画大部分的核心功能,包括计算各个帧的属性值,处理更新事件,按照属性值的类型控制计算规则等。
一个完整的属性动画由以下两部分组成。
计算动画各个帧的相关属性值
将这些属性设置给指定的对象

ValueAnimator为开发者实现了第一部分的功能,第二部分功能由开发者自行设置,ValueAnimator的构造函数是空实现,一般都是使用如下的静态工程方法来进行实例化。

  public ValueAnimator() {
}

public static ValueAnimator ofArgb(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    anim.setEvaluator(ArgbEvaluator.getInstance());
    return anim;
}
public static ValueAnimator ofFloat(float... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setFloatValues(values);
    return anim;
}
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setValues(values);
    return anim;
}
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setObjectValues(values);
    anim.setEvaluator(evaluator);
    return anim;
}

获取到实例后,接着需要设置动画持续时间、插值方式、重复次数等属性值,然后启动动画,最后还需要为ValueAnimator注册AnimatorUpdateListenner监听器,并在这个监听器的onAnimationUpdate方法中将计算出来的值设置给对象。我们从React Native这个开源框架中可以看到如下用法:


image.png

1.4.4 ObjectAnimator
ObjectAnimator是ValueAnimator的子类,封装实现了上面所说的第二部分的功能。因此,在实际开发中用到最多的就是ObjectAnimator,只有在ObjectAnimator实现不了的场景下,才考虑使用ValueAnimator。ObjectAnimator和ValueAnimator在构造实例时最大的不同是指需要指定动画作用的具体对象和对象属性名,而且一般不需要注册AnimatorUpdateListener监听器,简单的旋转View实例如下:

  public void rotateyAnimRun(View view)  
{  
     ObjectAnimator//  
     .ofFloat(view, "rotationX", 0.0F, 360.0F)//  
     .setDuration(500)//  
     .start();  
}  

那么对于属性动画简单介绍就到此告一段落,我们使用的越多就越能了解到它的强大之处,上面主要是讲述属性动画一些概念和简单实用方式,而接下来我们通过几个demo来知道属性动画还可以怎么用,实现什么样的效果。

实际开发当中,我们使用一下ViewPropertyAnimator来实现各种动画效果,而且是在是简单到难以想象。

使用方式:view.animate()后直接跟translation和alpha等想实现的效果
ImageView imageView = findViewById(R.id.iv);
imageView.animate().translationX(500).translationY(700).alpha(1f).setDuration(1000 );

我们看看实际操作方法有哪些?


出自Hencoder.png
话不多说先写一个出自Hencoder的Demo
进度.gif
public class SportView extends View{


public float getProgress() {
    return progress;
}

public void setProgress(float progress) {
    this.progress = progress;
    invalidate();
}

float progress = 0;

RectF arcRectF = new RectF();
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);


public SportView(Context context) {
    this(context,null);
}

public SportView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs,0);
}

public SportView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


{
    paint.setTextSize(Utils.dpToPixel(30));
    paint.setTextAlign(Paint.Align.CENTER);
}


@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //圆心位置
    float centerX = getWidth()/2;
    float centerY = getHeight()/2;
    //注意因为圆弧也有高度的原因,半径应该取设置小于宽度的一半
    float radius = getWidth()/5*2;

    paint.setColor(Color.parseColor("#E91E63"));
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeCap(Paint.Cap.ROUND);
    paint.setStrokeWidth(Utils.dpToPixel(10));
    //设置区域,四个参数对应四个点坐标
    arcRectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
    canvas.drawArc(arcRectF, 135, progress*2.7f, false, paint);

    paint.setColor(Color.WHITE);
    paint.setStyle(Paint.Style.FILL);
    canvas.drawText((int)progress+"%",centerX,centerY-(paint.ascent()+paint.descent())/5*2,paint);
}}

xml中直接引用我们的自定义控件:

  <com.example.xjf.androidanimation.SportView
    android:id="@+id/sportView"
    android:layout_width="160dp"
    android:layout_height="160dp" />

最后在Activity中直接创建一个ObjectAnimator对象并传入我们的进度参数绑定View:

 SportView sportView = findViewById(R.id.sportView);
    ObjectAnimator animator = ObjectAnimator.ofFloat(sportView,"progress",0,65);
    animator.start();

通用功能

  1. setDuration(int duration) 设置动画时长

单位是毫秒。

// imageView1: 500 毫秒
imageView1.animate()  [图片上传中...(AccelerateDecelerateInterpolator.gif-277ef4-1514647894330-0)]

    .translationX(500)
    .setDuration(500);

// imageView2: 2 秒
ObjectAnimator animator = ObjectAnimator.ofFloat(  
    imageView2, "translationX", 500);
animator.setDuration(2000);  
animator.start();  
不同时长效果.gif
  1. setInterpolator(Interpolator interpolator) 设置 Interpolator
 // imageView1: 线性 Interpolator,匀速
  imageView1.animate()  
    .translationX(500)
    .setInterpolator(new LinearInterpolator());

// imageView: 带施法前摇和回弹的 Interpolator
ObjectAnimator animator = ObjectAnimator.ofFloat(  
    imageView2, "translationX", 500);
animator.setInterpolator(new AnticipateOvershootInterpolator());  
animator.start();  
linearInterpolator.gif

接下来简单介绍一下每一个Interpolator:

AccelerateDecelerateInterpolator

先加速再减速。这是默认的 Interpolator,也就是说如果你不设置的话,那么动画将会使用这个 Interpolator。

这个是一种最符合现实中物体运动的 Interpolator,它的动画效果看起来就像是物体从速度为 0 开始逐渐加速,然后再逐渐减速直到 0 的运动。它的速度 / 时间曲线以及动画完成度 / 时间曲线都是一条正弦 / 余弦曲线。具体的效果如下:


AccelerateDecelerateInterpolator.gif

LinearInterpolator

匀速。


匀速.gif

AccelerateInterpolator

持续加速。

在整个动画过程中,一直在加速,直到动画结束的一瞬间,直接停止。

别看见它加速骤停就觉得这是个神经病模型哦,它很有用的。它主要用在离场效果中,比如某个物体从界面中飞离,就可以用这种效果。它给人的感觉就会是「这货从零起步,加速飞走了」。到了最后动画骤停的时候,物体已经飞出用户视野,看不到了,所以他们是并不会察觉到这个骤停的。

DecelerateInterpolator

持续减速直到 0。

动画开始的时候是最高速度,然后在动画过程中逐渐减速,直到动画结束的时候恰好减速到 0。
DecelerateInterpolator.gif

它的效果和上面这个 AccelerateInterpolator 相反,适用场景也和它相反:它主要用于入场效果,比如某个物体从界面的外部飞入界面后停在某处。它给人的感觉会是「咦飞进来个东西,让我仔细看看,哦原来是 XXX」。

AnticipateInterpolator

先回拉一下再进行正常动画轨迹。效果看起来有点像投掷物体或跳跃等动作前的蓄力。
AnticipateInterpolator.gif

如果是图中这样的平移动画,那么就是位置上的回拉;如果是放大动画,那么就是先缩小一下再放大;其他类型的动画同理。

这个 Interpolator 就有点耍花样了。没有通用的适用场景,根据具体需求和设计师的偏好而定。

OvershootInterpolator

动画会超过目标值一些,然后再弹回来。效果看起来有点像你一屁股坐在沙发上后又被弹起来一点的感觉。


OvershootInterpolator.gif

BounceInterpolator

在目标值处弹跳。有点像玻璃球掉在地板上的效果。


BounceInterpolator.gif

CycleInterpolator

这个也是一个正弦 / 余弦曲线,不过它和 AccelerateDecelerateInterpolator 的区别是,它可以自定义曲线的周期,所以动画可以不到终点就结束,也可以到达终点后回弹,回弹的次数由曲线的周期决定,曲线的周期由 CycleInterpolator() 构造方法的参数决定。

参数为 0.5f:
0.5f.gif

参数为2f:
2f.gif

PathInterpolator

自定义动画完成度 / 时间完成度曲线。

用这个 Interpolator 你可以定制出任何你想要的速度模型。定制的方式是使用一个 Path 对象来绘制出你要的动画完成度 / 时间完成度曲线。例如:

Path interpolatorPath = new Path();

...

// 匀速
interpolatorPath.lineTo(1, 1);  
image.png
006tKfTcly1fj8ffqb46bg30lg0buduv.gif
Path interpolatorPath = new Path();

...

// 先以「动画完成度 : 时间完成度 = 1 : 1」的速度匀速运行 25%
interpolatorPath.lineTo(0.25f, 0.25f);  
// 然后瞬间跳跃到 150% 的动画完成度
interpolatorPath.moveTo(0.25f, 1.5f);  
// 再匀速倒车,返回到目标点
interpolatorPath.lineTo(1, 1);  
image.png

006tKfTcly1fj8jsmxr3eg30lg0buto5.gif

本篇文章到此结束啦,感谢Hencoder开源的自定义View学习资料以及Android高级进阶一书所讲解的知识。本文都是出自上面文章,自己算作学习资料吧,下次更新一个比较完整的动画效果来总结一下,以及补上5.0之后的一些动画新特性,再见。

感谢:http://hencoder.com/
感谢:Android高级进阶

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

推荐阅读更多精彩内容