# Property Animation
## 屬性動畫的優點
- 屬性動畫顧名思義就是改變了View的屬性,而不僅僅是繪制的位置。
- 屬性動畫可以操作的屬性相比于補間動畫大大增加,除了常用的平移、旋轉、縮放、透明度還有顏色等,基本上能通過View.setXX來設置的屬性,屬性動畫都可以操作,這大大增加了我們在使用動畫時的靈活性。
- 屬性動畫分為ObjectAnimator和ValueAnimator,其中ObjectAnimator是繼承于ValueAnimator。
## ValueAnimator
> ValueAnimator并不會改變屬性的大小,他只是在一段時間生成某些值。我們需要做的是監聽這些值得改變從而該改變View的屬性,進而產生動畫效果。
下邊的動畫就是對mView進行平移:
```
ValueAnimator animator = ValueAnimator.ofFloat(0, 1000);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mView.setTranslationX(animation.getAnimatedValue());
}
});
animator.setDuration(1000).start()
```
## ObjectAnimator
> 在ValueAnimator的基礎之上,對控件的某個屬性執行一次動畫。
相同的對mView進行平移的動畫ObjectAnimator是這樣實現的:
```
ObjectAnimator animator=ObjectAnimator.ofFloat (mView,"translationX",0,1000);
animator.setDuration (1000);
animator.start ();
```
## PropertyAnimation流程圖

## 屬性動畫代碼的執行過程
### start
#### ObjectAnimator.start
```
public final class ObjectAnimator extends ValueAnimator {
/***部分代碼省略***/
@Override
public void start() {
//首先依次判斷了當前動畫、等待的動畫、延遲的動畫中是否有和當前動畫相同的動畫
//若有就把相同的動畫取消掉
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
if (handler != null) {
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();
}
}
}
/***部分代碼省略***/
}
/***部分代碼省略***/
//然后調用ValueAnimator.start()方法
super.start();
}
}
```
#### ValueAnimator.start
```
public class ValueAnimator extends Animator {
/***部分代碼省略***/
protected static ThreadLocal<AnimationHandler> sAnimationHandler =
new ThreadLocal<AnimationHandler>();
//保證每個線程有且只有一個AnimationHandler
private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {
handler = new AnimationHandler();
sAnimationHandler.set(handler);
}
return handler;
}
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
/***部分代碼省略***/
//創建或者獲取animationHandler實例
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();
}
//回調監聽器,通知動畫開始
private void notifyStartListeners() {
if (mListeners != null && !mStartListenersCalled) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this);
}
}
mStartListenersCalled = true;
}
public void setCurrentPlayTime(long playTime) {
float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
setCurrentFraction(fraction);
}
public void setCurrentFraction(float fraction) {
//初始化動畫
initAnimation();
if (fraction < 0) {
fraction = 0;
}
/***部分代碼省略***/
}
}
```
#### AnimationHandler.start
```
public class ValueAnimator extends Animator {
/***部分代碼省略***/
protected static class AnimationHandler implements Runnable {
/***部分代碼省略***/
//開始動畫
public void start() {
scheduleAnimation();
}
//發送VSYNC信號回調請求
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mAnimationScheduled = true;
}
}
// Called by the Choreographer.
//Choreographer的VSYNC信號回調
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
private void doAnimationFrame(long frameTime) {
/***部分代碼省略***/
// 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);
//執行動畫
//doAnimationFrame方法返回ture,則該動畫添加在mEndingAnims隊列中進行end操作
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
/***部分代碼省略***/
//循環執行,直到endAnimation將mAnimations置空
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
}
}
```
### init
#### ObjectAnimator.initAnimation
```
public final class ObjectAnimator extends ValueAnimator {
/***部分代碼省略***/
@Override
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
}
```
#### setupSetterAndGetter
```
public final class ObjectAnimator extends ValueAnimator {
/***部分代碼省略***/
void setupSetterAndGetter(Object target) {
mKeyframes.invalidateCache();
if (mProperty != null) {
/***部分代碼省略***/
}
// We can't just say 'else' here because the catch statement sets mProperty to null.
if (mProperty == null) {
Class targetClass = target.getClass();
if (mSetter == null) {
//初始化mSetter
setupSetter(targetClass);
}
/***部分代碼省略***/
}
}
//初始化mSetter用于以后反射執行get、set操作
void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
}
```
### animation
#### ValueAnimator.doAnimationFrame
```
public class ValueAnimator extends Animator {
/***部分代碼省略***/
final boolean doAnimationFrame(long frameTime) {
/***部分代碼省略***/
return animationFrame(currentTime);
}
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
/***部分代碼省略***/
if (fraction >= 1f) {
//mCurrentIteration是否等于mRepeatCount
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat
/***部分代碼省略***/
} else {
//執行完這次,該動畫結束
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
//設置View的屬性值
animateValue(fraction);
break;
}
return done;
}
}
```
#### ValueAnimator.animateValue
```
public class ValueAnimator extends Animator {
/***部分代碼省略***/
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//PropertyValuesHolder.calculateValue就是計算每幀動畫所對應的值
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
//屬性值得改變的回調
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
}
```
#### ObjectAnimator.animateValue
```
public final class ObjectAnimator extends ValueAnimator {
/***部分代碼省略***/
@Override
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up.
cancel();
return;
}
//ValueAnimator.animateValue方法
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//設置target的屬性值,進行View的移動,產生動畫
mValues[i].setAnimatedValue(target);
}
}
}
```
## PropertyValuesHolder
> PropertyValuesHolder這個類的意義就是,它其中保存了動畫過程中所需要操作的屬性和對應的值。我們通過ofFloat(Object target, String propertyName, float… values)構造的動畫,ofFloat()的內部實現其實就是將傳進來的參數封裝成PropertyValuesHolder實例來保存動畫狀態。在封裝成PropertyValuesHolder實例以后,后期的各種操作也是以PropertyValuesHolder為主的。
### ObjectAnimator.ofFloat
我們先看看我們之前的代碼中構造ObjectAnimator的方法:
```
public final class ObjectAnimator extends ValueAnimator {
/***部分代碼省略***/
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
//構造ObjectAnimator
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
//設置屬性值
public void setPropertyName(@NonNull String propertyName) {
/***部分代碼省略***/
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
@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);
}
}
}
```
### 構造FloatPropertyValueHolder
```
public class PropertyValuesHolder implements Cloneable {
/***部分代碼省略***/
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
}
```
```
public class PropertyValuesHolder implements Cloneable {
/***部分代碼省略***/
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
/***部分代碼省略***/
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
@Override
void setAnimatedValue(Object target) {
/***部分代碼省略***/
if (mSetter != null) {
try {
mTmpValueArray[0] = mFloatAnimatedValue;
//反射操作target的屬性,通過set、get方法
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
```
## 屬性動畫的內存泄露
- 上面講述到 `ValueAnimator.AnimationHandler.doAnimationFrame` 的時候說過,這個方法會循環執行。
- 因為 `ValueAnimator.AnimationHandler.doAnimationFrame` 每次執行完動畫(如果動畫沒有結束),都在再一次請求Vsync同步信號回調給自己。
- `Choreographer` 的回調都配post進入了當前線程的looper隊列中。
- `mRepeatCount` 無窮大,會導致該循環會一直執行下去,即使關閉當前的頁面也不會停止。
- 寫在前面的話
- Java
- 基礎
- Double的比較
- 小數怎么用二進制表示
- 多線程
- 并發和并行
- 線程池
- 線程池背景
- 線程池構造
- 任務阻塞隊列
- Flutter
- 基礎知識
- Dart基礎
- Android
- 項目架構
- View
- 非UI線程更新View
- AlarmManager
- 對比postDelaryed和Timer
- Bitmap
- 加載100M的圖片卻不撐爆內存
- Bitmap壓縮
- Bitmap局部解碼
- 計算圖片的內存占用
- Android動畫
- Android動畫類型
- Android動畫原理
- 屬性動畫
- 幀動畫
- 補間動畫
- 使用動畫的注意事項
- Android新特性
- 權限組
- Android23(Marshmallow)-6.0
- Android24(Nougat)-7.0
- Android26(Oreo)-8.0
- Android28(Pie)-9.0
- Android29(Q)-10.0
- AndroidX遷移
- Kotlin
- 關鍵字
- Kotlin操作符
- CoroutineScope
- Flow
- CoroutineException