# 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流程图
![](../images/PropertyAnimation流程图.png)
## 属性动画代码的执行过程
### 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