android屬性動畫學(xué)習(xí)與整理

對于android手機上的動畫實現(xiàn)主要有三種,一種是幀動畫,一種是View動畫,以及3.0以上提供的屬性動畫,所有View動畫可以實現(xiàn)的動畫都可以使用屬性動畫來實現(xiàn),最簡單的如漸變,平移,縮小和放大。

這里對于屬性動畫做個學(xué)習(xí)的總結(jié)與整理,以前總是學(xué)習(xí)過后直接使用,過一段時間后又忘記了如何去使用一個屬性動畫來設(shè)計自己的動畫效果,因此產(chǎn)生了這篇文章。

本篇文章分如下幾個部分:

  • ObjectAnimator介紹
  • ValueAnimator介紹
  • TypeEvaluator(估值器)和TimeInterpolator(插值器)的學(xué)習(xí)
  • 關(guān)于源碼的淺析

<h3>1.ObjectAnimator介紹</h3>
ObjectAnimator類是我們在設(shè)計動畫的時候經(jīng)常需要使用到的類,它可以直接對任意對象的任意屬性進行動畫操作,ObjectAnimator提供靜態(tài)的動畫實現(xiàn)方法,具體的實現(xiàn)方法有如下幾種:

下述的靜態(tài)方法中,傳入的參數(shù)一是目標(biāo)View,參數(shù)二是進行改變的屬性,參數(shù)三是變化的過程區(qū)間
請對照例子查看。

//顏色漸變動畫
public static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
//int類型值動畫
public static ObjectAnimator ofInt(Object target, String propertyName, int... values)
//float類型值動畫
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
//Object類型值動畫
public static ObjectAnimator ofObject(Object target, String propertyName,        TypeEvaluator evaluator, Object... values)

....

上述列舉了幾個基本的方法來使用ObjectAnimator類,下面舉兩個例子整理一下大概實現(xiàn)代碼的思路:

1.translationX

btn.setOnClickListener(new View.OnClickListener() {    
  @Override    
  public void onClick(View v) {       
       float s=imageView.getTranslationX();             
       ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX",s,-500f,s+100 ); 
       objectAnimator.setDuration(4000).start();   
 }});

效果圖

translationX.gif

上述實現(xiàn)的是一個基本是平移動畫,可以看出實現(xiàn)的效果跟View動畫如出一轍
alpha

btn.setOnClickListener(new View.OnClickListener() {      
   @Override   
  public void onClick(View v) {       
     ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f);        
     objectAnimator.setDuration(5000).start();    
}});

效果圖


alpha.gif

ObjectAnimator提供了不少方法設(shè)計動畫的執(zhí)行過程,下面舉出常用的一些方法:

//設(shè)置動畫執(zhí)行時長
setDuration(4000);
//設(shè)置動畫監(jiān)聽
objectAnimator.addListener(new Animator.AnimatorListener() {  
  @Override   
 public void onAnimationStart(Animator animation) { 
           }   
     @Override  
  public void onAnimationEnd(Animator animation) {  
  }    
    @Override  
  public void onAnimationCancel(Animator animation) {   
 }    
    @Override
    public void onAnimationRepeat(Animator animation) { 
   }});
//設(shè)置動畫循環(huán)播放的次數(shù),默認(rèn)為0,-1表示無限循環(huán)。
setRepeatCount();
//循環(huán)播放的模式,循環(huán)模式包括RESTART和REVERSE兩種,分別表示連續(xù)重新播放和倒序播放的意思
setRepeatMode();

上述的兩個例子只是針對于單個動畫的效果,如果想要組合起各個動畫,這也是各個復(fù)雜動畫產(chǎn)生的過程,那該如何使用呢?答案是AnimatorSet類的使用,AnimatorSet可以把動畫集合起來,既可以依次播放,也可以一起播放,可以定義各個動畫的播放順序,使用例子如下圖所示:

yo.gif

其代碼如下所示:

float x=view.getTranslationX();
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView, "translationX",x,500,x );
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f);
AnimatorSet animatorSet=new AnimatorSet();
animatorSet.playTogether(objectAnimator,objectAnimator2);
animatorSet.setDuration(5000);
animatorSet.start();

關(guān)于AnimatorSet的使用,有如下主要幾個常用的方法:

after(Animator anim)   將現(xiàn)有動畫插入到傳入的動畫之后執(zhí)行
after(long delay)   將現(xiàn)有動畫延遲指定毫秒后執(zhí)行
before(Animator anim)   將現(xiàn)有動畫插入到傳入的動畫之前執(zhí)行
with(Animator anim)   將現(xiàn)有動畫和傳入的動畫同時執(zhí)行
playTogether(Animator... items)將所有傳入動畫同時執(zhí)行
playSequentially(Animator... items)所有動畫依次執(zhí)行

通過組合不同的動畫,我們就可以寫出不同的復(fù)雜的動畫效果。

一個問題
為何在ObjectAnimator在第二個參數(shù)中傳入一個String值,目標(biāo)View就會執(zhí)行相對應(yīng)的動畫呢?如我們傳入一個alpha參數(shù),為什么就行了呢?

引用郭霖大神的解釋:其實ObjectAnimator內(nèi)部的工作機制并不是直接對我們傳入的屬性名進行操作的,而是會去尋找這個屬性名對應(yīng)的get和set方法,因此alpha屬性所對應(yīng)的get和set方法應(yīng)該就是:

public void setAlpha(float value);  
public float getAlpha();  

那么textview對象中是否有這兩個方法呢?確實有,并且這兩個方法是由View對象提供的,也就是說不僅TextView可以使用這個屬性來進行淡入淡出動畫操作,任何繼承自View的對象都可以的。
既然alpha是這個樣子,相信大家一定已經(jīng)明白了,前面我們所用的所有屬性都是這個工作原理,那么View當(dāng)中一定也存在著setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()這些方法。

那假設(shè)我們不傳入一個規(guī)定的值,我們隨便傳入一個字符串,如"aaaaa",他的效果會怎樣的?一般情況下動畫沒有響應(yīng),而嚴(yán)重的情況則可能造成程序的crash,因為屬性動畫是要求動畫對象提供get和set的方法,屬性動畫根據(jù)外界傳遞的屬性的廚師值和最終值,以動畫的效果多次的去調(diào)用set方法,每次傳遞給set的方法,隨著時間的沖突,所傳遞的值最終越來越接近最終值,因此我們隊object做動畫,如果想要讓動畫生效,要滿足兩個條件:

  • object提供setAbc方法,如果動畫時沒有傳遞初始值,那么還要提供getAbc方法,因為系統(tǒng)要去取abc屬性的值(不滿足此條,程序會直接Crash)。

  • object的setAbc對屬性abc所做的改變必須通過某種方法映射出來,如帶來UI的改變之類的(不滿足此條,動畫無效果但不會Crash)。


ValueAnimator

如果你大概略過一遍關(guān)于ObjectAnimator的源碼,你會發(fā)現(xiàn)其實ObjectAnimator是繼承自ValueAnimator的,也就是說其實屬性動畫的實現(xiàn)最核心的類應(yīng)該是ValueAnimator,而ObjectAnimator只是對其的一個封裝繼承而已,下面就先來了解一下關(guān)于ValueAnimator把。

下面代碼重新的實現(xiàn)了透明度的變化從1f->0f->1f的過程,跟ObjectAnimator第一個示例的效果一樣,這里就不貼gif圖片了:

            public void onClick(View v) {
                  valueAnimator=ValueAnimator.ofFloat(1F,0F,1F);      
                  valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                    
                    @Override                   
                    public void onAnimationUpdate(ValueAnimator animation) {                       
                         float result= (float) animation.getAnimatedValue();   
                         imageView.setAlpha(result);                    }                
                  });              
                  valueAnimator.setDuration(4000);                              
                  valueAnimator.start();            
      }      
  });

ValueAnimator并不負(fù)責(zé)持有需要做動畫的View對象的引用,而只是執(zhí)行了計算的過程,但是我們可以在addUpdateListener監(jiān)聽事件中進行設(shè)置View對象的變化過程,從而實現(xiàn)動畫的過程。所有使用ObjectAnimator實現(xiàn)的效果,我們都可以通過ValueAnimator來實現(xiàn)這個過程,這里只是提供了一種ValueAnimator的使用情況,在實際開發(fā)中,我們還是使用ObjectAnimator偏多。

關(guān)于ValueAnimator的常用方法,只要ObjectAnimator擁有的方法,ValueAnimator都有,這里就不一一列舉說明,有興趣的朋友可以詳細(xì)查看一下源碼。


<h4>TimeInterpolator(插值器)</h4>
TimeInterpolator中文譯為時間插值器,他的作用是根據(jù)時間流逝的百分比來計算出當(dāng)前屬性改變的百分比,系統(tǒng)中有預(yù)置的三種插值器:

  • LinearInterpolator(線性插值器),勻速的動畫
  • DecelerateInterpolator(減速插值器),動畫越來越慢
  • AccelerateDecelerateInterpolator(加速減速插值器),兩頭慢中間快

<h4>TypeEvaluator(估值器)</h4>
TypeEvaluator作用是根據(jù)當(dāng)前屬性改變的百分比來計算改變后的屬性值,其是要跟TimeInterpolator配對使用的。android內(nèi)置的估值器有:

  • IntEvaluator Int類型估值器,返回int類型的屬性改變
  • FloatEvaluator Float類型估值器,返回Float類型屬性改變
  • ArgbEvaluator 顏色類型估值器

TypeEvaluator源碼:


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

evaluate()方法當(dāng)中傳入了三個參數(shù),第一個參數(shù)fraction非常重要,這個參數(shù)用于表示動畫的完成度的,我們應(yīng)該根據(jù)它來計算當(dāng)前動畫的值應(yīng)該是多少,第二第三個參數(shù)分別表示動畫的初始值和結(jié)束值

下面以勻速動畫實現(xiàn)過程來研究估值器和插值器在其中所扮演的角色,關(guān)于勻速動畫實現(xiàn)的插值器源碼和估值器源碼如下所示:

時間插值器

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {  
 public LinearInterpolator() {    }   
 public LinearInterpolator(Context context, AttributeSet attrs) {    }   
 public float getInterpolation(float input) {        return input;    }  
      /** @hide */   
     @Override    
 public long createNativeInterpolator() {       
       return NativeInterpolatorFactoryHelper.createLinearInterpolator();    
  }
}

類型估值器
public class IntEvaluator implements TypeEvaluator<Integer> {    
  public Integer evaluate(float fraction, Integer startValue, Integer endValue) {     
     int startInt = startValue;    
     return (int)(startInt + fraction * (endValue - startInt));   
 }

比如一個移動的線性動畫需要在1s中完成400dp的移動過程,那么在0.4s時候,時間插值器的值是多少呢,主要是看其getInterpolation()方法,我們可以看到,過了0.4s,流逝的時間比即是0.4/1=0.4,所以屬性改變百分比的值為0.4,然后交付給估值器(這里假設(shè)使用Int類型估值器)進行對當(dāng)前對象屬性的改變操作。估值器獲取到當(dāng)前屬性改變的百分比0.4后,在IntEvaluator中會將第一個參數(shù)賦值為0.4,計算得到結(jié)果后在進行屬性的改變,當(dāng)前過程會一直重復(fù),知道動畫完成為止。

Android系統(tǒng)默認(rèn)提供的插值器和估值器滿足大部分使用場景,但是在某些情況下,我們可能需要重寫估值器或者插值器來達到使用動畫的目的,下面以重寫一個估值器為例:

定義一個動畫,使得View圍繞某一點做圓周運動,如圖所示:

yo.gif

當(dāng)然,直接通過重寫View的onDraw()實現(xiàn)起來更加簡單點,這里只是做個例子,在例子中,系統(tǒng)內(nèi)置的估值器并不能滿足我們的需求,所以這里進行重新定義,代碼如下:

自定義的估值器:

public class CircleTypeInterpolator implements TypeEvaluator {  
  private float radius;
  public CircleTypeInterpolator(float radius){    
    this.radius=radius;  
  }   
   @Override  
  public Object evaluate(float fraction, Object startValue, Object endValue) {       
   Point point=new Point();      
   Point startPoint= (Point) startValue;       
   //旋轉(zhuǎn)的角度       
   float angle=fraction*360;         
   float longY=(float) ((radius*Math.sin(Math.toRadians(angle))));  
   point.setY(startPoint.getY()+longY );            
   float longX= (float) (radius-radius*Math.cos(Math.toRadians(angle)));            point.setX(startPoint.getX()+longX);         
   return point; 
   }
}

這里主要涉及到關(guān)于計算的問題,計算的思路是通過起始點通過三角計算來得到新的點的坐標(biāo)。

自定義View:

public class CirlceView extends View {    
  private Paint paint;   
  private Point point;    
  Point startPoint; 
  public static final float RADIUS=30f;    
  public CirlceView(Context context) {       
       super(context); 
       init();  
  }    
  public CirlceView(Context context, AttributeSet attrs) { 
       super(context, attrs);        
       init();    
  }    
  private void init() {        
      paint=new Paint(Paint.ANTI_ALIAS_FLAG);  
      paint.setColor(Color.BLUE);    
  }    
    @Override   
  protected void onDraw(Canvas canvas) {      
    if (point==null){          
      point=new Point(getMeasuredWidth()/2,getMeasuredHeight()/2);  
      canvas.drawCircle(point.x,point.y,RADIUS,paint);            startAnim();        
    }else {            
      canvas.drawCircle(point.getX(),point.getY(),RADIUS,paint);        
    }   
  }   
   
  private void startAnim() {        
      try {            
            startPoint = (Point) point.clone();       
           } catch (CloneNotSupportedException e) {  
                e.printStackTrace();       
         }  
      ValueAnimator animator=ValueAnimator.ofObject(new CircleTypeInterpolator(100f),startPoint,startPoint);       
      animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            
         @Override         
       public void onAnimationUpdate(ValueAnimator animation) {    
            Point result= (Point) animation.getAnimatedValue();     
            point.setX(result.getX());                
            point.setY(result.getY());                
            invalidate();           
     }        
      });        
       animator.setDuration(2000);        
       animator.setRepeatCount(-1);        
       animator.start();   
 }
}

自定義對象:

  public class Point implements  Cloneable {    
        public float x;    
        public float y;    
        public Point(float x, float y) {       
           this.x = x;        this.y = y;    
        }    
        public Point() {    }    
        public float getX() {        
                return x;    
        }    
        public void setX(float x) {        
              this.x = x;    
        }   
        public float getY() {        
              return y;    
        }   
        public void setY(float y) {      
            this.y = y;    
        } 
         @Override    
        protected Object clone() throws CloneNotSupportedException {      
            Point point=null;       
            point= (Point) super.clone();      
           return point;   
        }

}

<h3>源碼淺析</h3>
上面整理了屬性的動畫的使用的具體的一些方法以及例子,這里再對于屬性動畫進行源碼方面的研究,不求有多精通深入,希望能對動畫源碼有一個大體的認(rèn)識,方便日后研究。

屬性動畫工作原理

屬性動畫要求動畫作用的對象提供該屬性的set方法,蘇弧形動畫會根據(jù)你傳遞的該屬性的初始值和最終值,以動畫的效果多次調(diào)用set方法。每次傳遞個set方法的值東不一樣,隨著時間的推移,所傳遞的值越來越接近最終值。如果動畫沒有傳遞初始值,那么還要提供get方法,因為系統(tǒng)要去獲取屬性的初始值。

從ObjectAnimator的為起點進行分析,選擇ofFloat(...)進行代碼研究著手點:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }

private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }
//這里設(shè)置目標(biāo)target,并且做了判斷,如果當(dāng)前的target已經(jīng)啟動了動畫,則會將動畫取消。
public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            if (isStarted()) {
                cancel();
            }
            mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }
    
//設(shè)置屬性名
public void setPropertyName(@NonNull String propertyName) {
        // mValues could be null if this is being constructed piecemeal. Just record the
        // propertyName to be used later when setValues() is called if so.
        if (mValues != null) {
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
            mValuesMap.put(propertyName, valuesHolder);
        }
        mPropertyName = propertyName;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

這里構(gòu)建了一個ObjectAnimator對象,在ObjectAnimator對象中傳入了目標(biāo)target以及屬性名propertyName,然后在調(diào)用setFloatValues方法:

@Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

這里ObjectAnimator通過設(shè)置浮點值來調(diào)用父類ValueAnimator的setVaules函數(shù),先查看傳入的值:

 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
        return new FloatPropertyValuesHolder(propertyName, values);
    }

 public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
            setFloatValues(values);
        }

 @Override
        public void setFloatValues(float... values) {
            super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }
//對應(yīng) super.setFloatValues(values)初始化的東東
public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }

這里返回了一個FloatPropertyValuesHolder對象,F(xiàn)loatPropertyValuesHolder調(diào)用了setFloatValues()方法,在其中設(shè)置了mFloatKeyframes這個變量,mFloatKeyframes通過調(diào)用KeyframeSet.ofFloat(values)方法獲得,這里查看一下KeyframeSet.ofFloat(values):

public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
            if (Float.isNaN(values[0])) {
                badValue = true;
            }
        } else {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        if (badValue) {
            Log.w("Animator", "Bad value (NaN) in float animator");
        }
        return new FloatKeyframeSet(keyframes);
    }

這里的keyframe成為關(guān)鍵幀,而KeyframeSet則是存儲Keyframe的集合,代碼中根據(jù)values的長度來構(gòu)建一個FloatKeyframe keyframes[]數(shù)組,然后通過Keyframe.ofFloat()方法去構(gòu)造一個keyframe對象:

FloatKeyframe(float fraction, float value) {
            mFraction = fraction;
            mValue = value;
            mValueType = float.class;
            mHasValue = true;
        }

        FloatKeyframe(float fraction) {
            mFraction = fraction;
            mValueType = float.class;
        }

在初始化完成后返回keyframes數(shù)組,將其傳入到FloatKeyframeSet中進行構(gòu)建,查看FloatKeyframeSet的構(gòu)建方法:

public KeyframeSet(Keyframe... keyframes) {
        mNumKeyframes = keyframes.length;
        // immutable list
        mKeyframes = Arrays.asList(keyframes);
        mFirstKeyframe = keyframes[0];
        mLastKeyframe = keyframes[mNumKeyframes - 1];
        mInterpolator = mLastKeyframe.getInterpolator();
    }

FloatKeyframeSet最終只是調(diào)用了父類的初始化方法,在父類的初始化方法中存儲了所有的關(guān)鍵幀,開始幀,結(jié)束幀以及時間插值器。到這里,PropertyValuesHolder.ofFloat()初始化完成,重新回溯到ObjectAnimator.setFloatValue()中,調(diào)用其setValues(PropertyValuesHolder.ofFloat(mPropertyName, values))方法:

public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

首先記錄了mValues,這里的values是PropertyValuesHolder[]類型的,然后通過一個mValueMap記錄:key為屬性的名稱,值為PropertyValuesHolder 。

總結(jié)一下初始化的過程(摘自鴻洋csdn):ofFloat就是記錄了target,propName,values(是將我們傳入的int型values,輾轉(zhuǎn)轉(zhuǎn)化成了PropertyValuesHolder),以及一個mValueMap,這個map的key是propName,value是PropertyValuesHolder,在PropertyValuesHolder內(nèi)部又存儲了proprName, valueType , keyframeSet等等。

這里關(guān)于ObjectAnimator.ofFloat()方法的初始化就到此完成.

接下來著手研究一下核心的start()方法:

 public void start() {
        // See if any of the current active/pending animators need to be canceled
        AnimationHandler handler = sAnimationHandler.get();
        if (handler != null) {
            //當(dāng)前動畫
            int numAnims = handler.mAnimations.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
            //準(zhǔn)備執(zhí)行的動畫
            numAnims = handler.mPendingAnimations.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
            //延遲的動畫
            numAnims = handler.mDelayedAnims.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
        }
        .....
        super.start();
    }

    //是否存在相同的屬性和包名
    private boolean hasSameTargetAndProperties(@Nullable Animator anim) {
        if (anim instanceof ObjectAnimator) {
            PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
            if (((ObjectAnimator) anim).getTarget() == getTarget() &&
                    mValues.length == theirValues.length) {
                for (int i = 0; i < mValues.length; ++i) {
                    PropertyValuesHolder pvhMine = mValues[i];
                    PropertyValuesHolder pvhTheirs = theirValues[i];
                    if (pvhMine.getPropertyName() == null ||
                            !pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

在start()方法中通過AnimationHandler來獲得到當(dāng)前/準(zhǔn)備/刪除的動畫,通過hasSameTargetAndProperties()來判斷如果當(dāng)前/準(zhǔn)備/刪除動畫中有和當(dāng)前動畫相同的,那么就取消掉相同的動畫。

然后調(diào)用父類的start()方法,由于ObjectAnimator的父類是ValueAnimator,所以我們找到源碼:

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mPlayingBackwards = playBackwards;
        if (playBackwards && mSeekFraction != -1) {
            if (mSeekFraction == 0 && mCurrentIteration == 0) {
                // special case: reversing from seek-to-0 should act as if not seeked at all
                mSeekFraction = 0;
            } else if (mRepeatCount == INFINITE) {
                mSeekFraction = 1 - (mSeekFraction % 1);
            } else {
                mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
            }
            mCurrentIteration = (int) mSeekFraction;
            mSeekFraction = mSeekFraction % 1;
        }
        if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
                (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
            // if we were seeked to some other iteration in a reversing animator,
            // figure out the correct direction to start playing based on the iteration
            if (playBackwards) {
                mPlayingBackwards = (mCurrentIteration % 2) == 0;
            } else {
                mPlayingBackwards = (mCurrentIteration % 2) != 0;
            }
        }
        int prevPlayingState = mPlayingState;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        mPaused = false;
        updateScaledDuration(); // in case the scale factor has changed since creation time
        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            if (prevPlayingState != SEEKED) {
                setCurrentPlayTime(0);
            }
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();
    }

Animators需要在擁有Looper的線程中執(zhí)行,如果要更新UI,則必須在UI線程中使用。
在animationHandler(這是一個Runnable)中把當(dāng)前動畫加入到準(zhǔn)備動畫當(dāng)中,并且初始化一些動畫的狀態(tài)變量,最后調(diào)用animationHandler.start()方法啟動動畫,start()源碼:

      public void start() {
            scheduleAnimation();
        }

      private void scheduleAnimation() {
            if (!mAnimationScheduled) {
                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
                mAnimationScheduled = true;
            }
        }

最終會調(diào)用到Choreographer類,這是與底層交互的類,animationHandler是Runnable的子類,而 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);類似與handler發(fā)送消息,最終執(zhí)行這個Runnable的run方法。所以只要查看run方法即可:

    public void run() {
        mAnimationScheduled = false;
        doAnimationFrame(mChoreographer.getFrameTime());
    }

doAnimationFrame()方法:

void doAnimationFrame(long frameTime) {
            mLastFrameTime = frameTime;

            // mPendingAnimations holds any animations that have requested to be started
            // We're going to clear mPendingAnimations, but starting animation may
            // cause more to be added to the pending list (for example, if one animation
            // starting triggers another starting). So we loop until mPendingAnimations
            // is empty.
            while (mPendingAnimations.size() > 0) {
                ArrayList<ValueAnimator> pendingCopy =
                        (ArrayList<ValueAnimator>) mPendingAnimations.clone();
                mPendingAnimations.clear();
                int count = pendingCopy.size();
                for (int i = 0; i < count; ++i) {
                    ValueAnimator anim = pendingCopy.get(i);
                    // If the animation has a startDelay, place it on the delayed list
                    if (anim.mStartDelay == 0) {
                        anim.startAnimation(this);
                    } else {
                        mDelayedAnims.add(anim);
                    }
                }
            }

            // Next, process animations currently sitting on the delayed queue, adding
            // them to the active animations if they are ready
            int numDelayedAnims = mDelayedAnims.size();
            for (int i = 0; i < numDelayedAnims; ++i) {
                ValueAnimator anim = mDelayedAnims.get(i);
                if (anim.delayedAnimationFrame(frameTime)) {
                    mReadyAnims.add(anim);
                }
            }
            int numReadyAnims = mReadyAnims.size();
            if (numReadyAnims > 0) {
                for (int i = 0; i < numReadyAnims; ++i) {
                    ValueAnimator anim = mReadyAnims.get(i);
                    anim.startAnimation(this);
                    anim.mRunning = true;
                    mDelayedAnims.remove(anim);
                }
                mReadyAnims.clear();
            }

            // Now process all active animations. The return value from animationFrame()
            // tells the handler whether it should now be ended
            int numAnims = mAnimations.size();
            for (int i = 0; i < numAnims; ++i) {
                mTmpAnimations.add(mAnimations.get(i));
            }
            for (int i = 0; i < numAnims; ++i) {
                ValueAnimator anim = mTmpAnimations.get(i);
                if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
                    mEndingAnims.add(anim);
                }
            }
            mTmpAnimations.clear();
            if (mEndingAnims.size() > 0) {
                for (int i = 0; i < mEndingAnims.size(); ++i) {
                    mEndingAnims.get(i).endAnimation(this);
                }
                mEndingAnims.clear();
            }

            // Schedule final commit for the frame.
            mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);

            // If there are still active or delayed animations, schedule a future call to
            // onAnimate to process the next frame of the animations.
            if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
                scheduleAnimation();
            }
        }

在while循環(huán)當(dāng)中,首先將所有準(zhǔn)備動畫取出,然后清空準(zhǔn)備動畫的List,接著通過是否延遲播放來判斷立即執(zhí)行還是延遲執(zhí)行。將animationHandler的mAnimations集合中的每個anim,加入到mTmpAnimations中,依次調(diào)用mTmpAnimations中的anim,anim.doAnimationFrame(frameTime),doAnimationFrame(frameTime)上面已經(jīng)分析過了,如果返回true,即doAnimationFrame的done為true,則將該動畫加入到結(jié)束動畫集合。
循環(huán)調(diào)用mEndingAnims, mEndingAnims.get(i).endAnimation(this);內(nèi)部,會將動畫移除mAnimations,回調(diào)動畫監(jiān)聽接口onAnimationEnd;以及重置各種標(biāo)志變量。如果mAnimations不為null,則再次調(diào)用scheduleAnimation(),這里有調(diào)用到了scheduleAnimation,上述分析這是一個與底層交互的入口,因此循環(huán)調(diào)用使得view的屬性變化,直到動畫執(zhí)行完成為止。

總結(jié)

引用鴻洋大大的吧(博客鏈接)

ofInt中實例化了一個ObjectAnimator對象,然后設(shè)置了target,propName,values(PropertyValuesHolder) ;然后分別在setInterpolator,setDuration設(shè)置了Interpolator
和duration。其中setEvaluator是給PropertyValuesHolder,以及keyframeSet設(shè)置估值算法。
PropertyValueHolder實際上是IntPropertyValueHolder類型對象,包含propName,valueType,keyframeSet .

keyframeset中存了Keyframe集合,keyframe中存儲了(fraction , valuetype , value , hasValue)。
上述其實都是設(shè)置各種值什么的。真正核心要看start~
start()中:
首先,步驟1:更新動畫各種狀態(tài),然后初步計算fraction為(currentTime - mStartTime) / mDuration;然后將這個fraction交給我們的插值器計算后得到新的fraction,再將新的fraction交給我們的估值算法,估值算法根據(jù)開始、結(jié)束、fraction得到當(dāng)前屬性(動畫作用的屬性)應(yīng)該的值,最大調(diào)用反射進行設(shè)置;
當(dāng)然了:start中還會根據(jù)動畫的狀態(tài),如果沒有結(jié)束,不斷的調(diào)用scheduleAnimation();該方法內(nèi)部利用mChoreographer不斷的去重復(fù)我們的上述步驟1。


參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內(nèi)容