Android 属性动画(二)

上一篇 Android 属性动画(一) 介绍了Property Animator 的基本用法。今天继续了解Property Animator 的Evaluator、Interpolator 和 xml 文件定义Animator 的相关知识和用法。

Evaluator 求值器

直接贴源码:

public interface TypeEvaluator<T> {    
/**     
    * This function returns the result of linearly interpolating the start and end values, with   
    * <code>fraction</code> representing the proportion between the start and end values. The     
    * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,    
    * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,     
    * and <code>t</code> is <code>fraction</code>.     
    * 
    * @param fraction   The fraction from the starting to the ending values     
    * @param startValue The start value.     
    * @param endValue   The end value.     
    * @return A linear interpolation between the start and end values, given the     
    *         <code>fraction</code> parameter.     
    */    
public T evaluate(float fraction, T startValue, T endValue);
}

源码很简短,就一个方法,默认实现就是 根据fraction 通过 result = x0 + t * (x1 - x0) 公式计算返回一个中间值。
那么 fraction 这个参数是多少呢?通过ValueAnimator 的setEvaluator() 方法找到源码:

boolean animationFrame(long currentTime) {    
    boolean done = false;
    switch (mPlayingState) {
    case RUNNING:
    case SEEKED:
        float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
        if (mDuration == 0 && mRepeatCount != INFINITE) {
            // Skip to the end 
           mCurrentIteration = mRepeatCount;
            if (!mReversing) {
                mPlayingBackwards = false;
            }
        }
        if (fraction >= 1f) {
            if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE)
            {                
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
                if (mRepeatMode == REVERSE) {
                    mPlayingBackwards = !mPlayingBackwards;
                }
                mCurrentIteration += (int) fraction;
                fraction = fraction % 1f;
                mStartTime += mDuration;
                // Note: We do not need to update the value of mStartTimeCommitted here
                // since we just added a duration offset.
            } else {
                done = true;
                fraction = Math.min(fraction, 1.0f);
            }
        }
        if (mPlayingBackwards) {
            fraction = 1f - fraction;
        }
        animateValue(fraction);
        break;
    }
    return done;
}

void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}

在 ValueAnimator 类中有两个方法,可以看到在 animationFrame 方法就是 得到一个已完成时间在总时长的比例值(0~1)并赋值给 fraction,在 animateValue 方法中 调用了 mInterpolator.getInterpolation() (就是 插值器,后面会介绍) 目的就是修正 fraction 。所以我们知道 fraction 是一个0~1 的值。现在看下自定义Evaluator 的代码:

//定义了一个 PointF 类型的 Evaluator ,实现 view 的抛物线变化
//默认 TypeEvaluator View 在X,Y方向都是匀速的。
//自定义后,View的Y方向是变速的。

ValueAnimator valueAnimator = ValueAnimator.ofObject(new TypeEvaluator<PointF>(){
    @Override    public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
        // y 方向是一个变速        PointF point = new PointF();
        point.x = startValue.x + fraction * (endValue.x - startValue.x) ;
        point.y = (float) (startValue.y + Math.pow(fraction , 2) * (endValue.y - startValue.y));
        return point;
    }},
new PointF(0,0),new PointF(400,1000));
valueAnimator.setDuration(3000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
         @Override    
        public void onAnimationUpdate(ValueAnimator animation)    {
        PointF point = (PointF) animation.getAnimatedValue();
        animView.setX(point.x);
        animView.setY(point.y);
    }});
valueAnimator.start();

Interpolator 插值器

本来想写下Interpolayor的源码分析的,但看到一篇 Interpolayor的分析文章后,感觉自己也不会写的比这个好,所以还是不写了,借鉴和学习这篇文章 李海珍大神的 android动画(一)Interpolator。膜拜!!!
一下是从 大神文章中摘抄的部分,图文并茂,讲解很详细,大家学习下!

二:简单插值器分析
** 注意下面的图,对应你脑海中的插值的大小应该是斜率。**

package android.view.animation;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
/**
 *
 * 一个开始很慢然后不断加速的插值器。 
 * <hr/>
 * An interpolator where the rate of change starts out slowly and
 * and then accelerates.
 *
 */
public class AccelerateInterpolator implements Interpolator {
     private final float mFactor; private final double mDoubleFactor;
     public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
     }
     /**     
       * Constructor
       *
       * @param factor
       * 动画的快慢度。将factor设置为1.0f会产生一条y=x^2的抛物线。增加factor到1.0f之后为加大这种渐入效果(也就是说它开头更加慢,结尾更加快)
       * <br/>Degree to which the animation should be eased. Seting
       * factor to 1.0f produces a y=x^2 parabola(抛物线). Increasing factor above
       * 1.0f exaggerates the ease-in effect (i.e., it starts even   * slower and ends evens faster)
      */
      public AccelerateInterpolator(float factor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
      } 
      public AccelerateInterpolator(Context context, AttributeSet attrs) {
        TypedArray a =context.obtainStyledAttributes(attrs,com.android.internal.R.styleable.AccelerateInterpolator);
        mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
        mDoubleFactor = 2 * mFactor;
        a.recycle();
      }
      @Override 
      public float getInterpolation(float input) {
         if (mFactor == 1.0f) {
             return input * input;  
          } else {
              return (float)Math.pow(input, mDoubleFactor);
          } 
      }
}

加速的快慢度由参数fractor决定。
当fractor值为1.0f时,动画加速轨迹相当于一条y=x^2的抛物线。如下图:


当fractor不为1时,轨迹曲线是y=x^(2*fractor)(0<x<=1)的曲线。
示例:当fractor为4时,插值器的加速轨迹曲线如下图:

如果你在使用AccelerateInterpolator时,想要那种一开始很慢,然后突然就很快的加速的动画效果的话。
就将fractor设置大点。
你可以到这里调试下你想要的抛物线效果:http://www.wolframalpha.com/input/?i=x%5E%282*3%29%280%3Cx%3C%3D1%29

Android提供的一个不同factor的加速插值器:
(1)accelerate_cubic, factor为1.5

xml 文件定义Animator

大家应该都在xml 文件定义过 帧动画 和 补间动画。今天,我们就在xml中定义属性动画,来看看有什么区别。
先在 res 文件下创建 anmator 文件夹,在创建 res/animator/animator_set.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"    
    android:ordering="together">
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1"
        android:valueTo="0.5">
    </objectAnimator>
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleY"
        android:valueFrom="1"
        android:valueTo="0.5">
    </objectAnimator>
</set>

这个文件是实现 同时缩放XY轴的动画集合,我们可以看到 set 标签 动画集合有个
android:ordering 属性,android:ordering 有两个值 :together(同时执行),sequentially (顺序执行)。接下来在代码中加载动画:

// 加载动画
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator_set);
//设置 缩放的中心点
animView.setPivotX(0);
animView.setPivotY(0);
//显示的调用invalidate
animView.invalidate();
anim.setTarget(animView);
anim.start();

如上,将 animView 设为target 就可以了。 当然,缩放 和旋转都可以设置中心点的,我们将缩放的中心点设置在了 (0,0),默认都是在对象中心点。如果,只想实现单个动画,可以在xml 中去掉 set标签。

下面是总结
今天介绍了 Property Animator 的 Evaluator 、Interpolator 和 xml 定义,我们可以跟愉快的实现酷炫的动画啦。当然,在看了Interpolator 的文章后,还是佩服的五体投地的,我们要努力,像大神看齐!
接下来,我会总结下 ** 布局动画(Layout Animations)** ,然后就是 属性动画实战啦,233333。fighting!!!

源码,源码中还有我之前写的防直播打赏礼物动画效果。欢迎大家指教!

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

推荐阅读更多精彩内容

  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,690评论 0 10
  • Animation Animation类是所有动画(scale、alpha、translate、rotate)的基...
    四月一号阅读 1,899评论 0 10
  • 时常在想究竟是为什么让我进入了汽车行业? 这是我毕业后第一份正规职业,因为身体的原因,所以我选择的是人事和行政。可...
    小元子美眉阅读 406评论 0 1
  • 临近寒冬,白昼愈来愈短,晚上下班,出地铁站,临近八点,外面的世界除了寒意侵人,便是黑夜,深深的黑;灯火通明的地铁站...
    咖啡与蔷薇阅读 254评论 0 0
  • 很久以前,知道世上有个你。 让河流接住倒影,让鲜花及时怒放,让年华倾其所有,三十六岁的你还是画一般的模样。 人前你...
    青灰摇阅读 510评论 4 4