属性动画源码解析,大致的了解了下属性动画的原理,也对一些疑问做了下解答
属性动画源码解析
1.AminatorSet中每一个animator设置duration时间,跟AminatorSet单个设置duration时间的区别
//旋转的动画,
ObjectAnimator firstRotate1Anima = ObjectAnimator.ofFloat(mFirstView,"rotationX",0,20f);
firstRotate1Anima.setDuration(1000);
ObjectAnimator secondAnima = ObjectAnimator.ofFloat(mSecondView,"tranlationY",mSecondView.getHeight()
* 0.1f,0f);
secondAnima.setDuration(300);
secondAnima.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mSecondView.setVisibility(View.VISIBLE);
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(firstRotate1Anima,secondAnima);
animatorSet.start();
上面的体现是如果单独的每一个设置动画的执行的时间,这个对于每一个item都是有效的,比如第一个动画执行300毫秒,第二个动画执行500毫秒,
如果设置为animatorset.playTogether()的化,动画的开始就是一起的,如果后面AnimatorSet设置了duration时间,这个将会覆盖掉每一个item单独设置的时间,源码体现为
animatorSet.playTogether(firstRotate1Anima,secondAnima);
public void playTogether(Animator... items) {
if (items != null) {
//每一个item封装成一个builder对象
Builder builder = play(items[0]);
for (int i = 1; i < items.length; ++i) {
builder.with(items[i]);
}
}
}
animatorSet.start();
//源码实现
public void start() {
......
int size = mNodes.size();
//每一个都设置插值器,也就是animatorSet中设置的插值器也会覆盖掉每一个item单独设置的插值器
if (mInterpolator != null) {
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
node.mAnimation.setInterpolator(mInterpolator);
}
}
//更新每一个item动画的时间
updateAnimatorsDuration();
createDependencyGraph();
// Now that all dependencies are set up, start the animations that should be started.
boolean setIsEmpty = false;
if (mStartDelay > 0) {
start(mRootNode);
} else if (mNodes.size() > 1) {
// No delay, but there are other animators in the set
//一般执行这里
onChildAnimatorEnded(mDelayAnim);
} else {
// Set is empty, no delay, no other animation. Skip to end in this case
setIsEmpty = true;
}
}
updateAnimatorsDuration();
//源码的体现为
private void updateAnimatorsDuration() {
if (mDuration >= 0) { //这个值为AminatorSet中设置的duration
// If the duration was set on this AnimatorSet, pass it along to all child animations
int size = mNodes.size();
for (int i = 0; i < size; i++) {
Node node = mNodes.get(i);
// TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
// insert "play-after" delays
node.mAnimation.setDuration(mDuration);//所以会覆盖掉每一个item单独设置的时间
}
}
mDelayAnim.setDuration(mStartDelay);
}
onChildAnimatorEnded(mDelayAnim);
源码的体现为:
private void onChildAnimatorEnded(Animator animation) {
Node animNode = mNodeMap.get(animation);
animNode.mEnded = true;
if (!mTerminated) {
List<Node> children = animNode.mChildNodes;
// Start children animations, if any.
int childrenSize = children == null ? 0 : children.size();
for (int i = 0; i < childrenSize; i++) {
if (children.get(i).mLatestParent == animNode) {
start(children.get(i));//遍历每一个item,开始动画
}
}
。。。。。。
}
void start(final Node node) {
final Animator anim = node.mAnimation;
mPlayingSet.add(anim);
anim.addListener(mSetListener);
anim.start();//因为我们的每一个item都是ObjAminator,所以执行的是这个对象的start方法
}
@Override
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
Log.d(LOG_TAG, " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
pvh.mKeyframes.getValue(1));
}
}
super.start(); //这个super为ValueAnimator
}
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
.....
// Resets mLastFrameTime when start() is called, so that if the animation was running,
// calling start() would put the animation in the
// started-but-not-yet-reached-the-first-frame phase.
mLastFrameTime = 0;
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
if (mStartDelay == 0 || mSeekFraction >= 0) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
if (mSeekFraction == -1) {
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
源码体现:
/**
* Register to get a callback on the next frame after the delay.
*/
因为我们的delay为0
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
}
总之就会回调ValueAminator中的,设置的回调也即是
public final void doAnimationFrame(long frameTime) {
if (mLastFrameTime == 0) {
// First frame
handler.addOneShotCommitCallback(this);
if (mStartDelay > 0) {
startAnimation();
}
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
.....
}
startAnimation();的源码实现为
/**
* Called internally to start an animation by adding it to the active animations list. Must be
* called on the UI thread.
*/
private void startAnimation() {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
mAnimationEndRequested = false;
initAnimation();
mRunning = true;
if (mSeekFraction >= 0) {
mOverallFraction = mSeekFraction;
} else {
mOverallFraction = 0f;
}
if (mListeners != null) {
notifyStartListeners();
}
}
initAnimation();源码的实现为:注意这个方法要看具体的子类的实现,因为当前的子类的对象为ObjValueAmiator
@CallSuper
@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();
}
}
mValues[i].setupSetterAndGetter(target); 源码的实现为: 当前的mVlaus为PropertvalueHolder的集合
void setupSetterAndGetter(Object target) {
....
// We can't just say 'else' here because the catch statement sets mProperty to null.
if (mProperty == null) {
Class targetClass = target.getClass();
找到set对应的属性的方法
if (mSetter == null) {
setupSetter(targetClass);
}
再去找到这个属性对应的方法,去改变这个值
List<Keyframe> keyframes = mKeyframes.getKeyframes();
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
if (!kf.hasValue() || kf.valueWasSetOnStart()) {
if (mGetter == null) {
setupGetter(targetClass);得到这个属性对应的gett方法
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
try {
利用反射的原理去调用gett的方法,动态的修改这个属性对应的值
Object value = convertBack(mGetter.invoke(target));
kf.setValue(value);
kf.setValueWasSetOnStart(true);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
setupSetter(targetClass);源码的体现为:
/**
* Utility function to get the setter from targetClass
* @param targetClass The Class on which the requested method should exist.
*/
//属性方法的推导原理实现,因为为设置的是setter方法,所以这里的前缀为set
void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
private Method setupSetterOrGetter(Class targetClass,
HashMap<Class, HashMap<String, Method>> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null;
synchronized(propertyMapMap) {
// Have to lock property map prior to reading it, to guard against
// another thread putting something in there after we've checked it
// but before we've added an entry to it
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
boolean wasInMap = false;
if (propertyMap != null) {
wasInMap = propertyMap.containsKey(mPropertyName);
if (wasInMap) {
setterOrGetter = propertyMap.get(mPropertyName);
}
}
if (!wasInMap) {
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
propertyMapMap.put(targetClass, propertyMap);
}
propertyMap.put(mPropertyName, setterOrGetter);
}
}
return setterOrGetter;
}
etterOrGetter = getPropertyFunction(targetClass, prefix, valueType); 源码的解析
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
// TODO: faster implementation...
Method returnVal = null;
String methodName = getMethodName(prefix, mPropertyName);
Class args[] = null;
if (valueType == null) {
try {
returnVal = targetClass.getMethod(methodName, args); 可以看到是利用反射的原理去实现的
} catch (NoSuchMethodException e) {
// Swallow the error, log it later
这就是为什么我们随便的写一个属性的时候,不会出现崩溃的原因,这里就是帮我们处理的
}
} else {
args = new Class[1];
Class typeVariants[];
if (valueType.equals(Float.class)) {
typeVariants = FLOAT_VARIANTS;
} else if (valueType.equals(Integer.class)) {
typeVariants = INTEGER_VARIANTS;
} else if (valueType.equals(Double.class)) {
typeVariants = DOUBLE_VARIANTS;
} else {
typeVariants = new Class[1];
typeVariants[0] = valueType;
}
for (Class typeVariant : typeVariants) {
args[0] = typeVariant;
try {
returnVal = targetClass.getMethod(methodName, args);
if (mConverter == null) {
// change the value type to suit
mValueType = typeVariant;
}
return returnVal;
} catch (NoSuchMethodException e) {
// Swallow the error and keep trying other variants
}
}
// If we got here, then no appropriate function was found
}
if (returnVal == null) {
Log.w("PropertyValuesHolder", "Method " +
getMethodName(prefix, mPropertyName) + "() with type " + valueType +
" not found on target class " + targetClass);
}
return returnVal;
}
String methodName = getMethodName(prefix, mPropertyName)源码的体现为:
static String getMethodName(String prefix, String propertyName) {
if (propertyName == null || propertyName.length() == 0) {
// shouldn't get here
return prefix;
}
这里就是为什么我们的第一个属性的字母大写的时候为什么可以识别的到,第二个之后的就不可以的原因
char firstLetter = Character.toUpperCase(propertyName.charAt(0));
String theRest = propertyName.substring(1);
return prefix + firstLetter + theRest;
}