clearAnimation的简要流程
自己做了一个关于Animation动画的小游戏,出现了一些小问题,所以稍微看了一下此流程:
/**
* Cancels any animations for this view.
*/
public void clearAnimation() {
if (mCurrentAnimation != null) {
mCurrentAnimation.detach(); ===> ①触发onAnimationEnd()方法
}
mCurrentAnimation = null; ===> 将Animation置为null
invalidateParentIfNeeded(); ===> ④
}
==> mCurrentAnimation == Animation
①将Animation状态设置为Ended,并执行fireAnimationEnd()==>用于触发onAnimationEnd()方法
/**
* @hide
*/
@UnsupportedAppUsage
public void detach() {
if (mStarted && !mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd(); ===>②
}
}
==>设置状态
/**
* Set by {@link #getTransformation(long, Transformation)} when the animation starts.
*/
==> boolean mStarted = false;
/**
* Set by {@link #getTransformation(long, Transformation)} when the animation ends.
*/
==> boolean mEnded = false;
==> private final CloseGuard guard = CloseGuard.get();
②
private void fireAnimationEnd() {
if (hasAnimationListener()) {
if (mListenerHandler == null)
dispatchAnimationEnd(); ===> ③
else
mListenerHandler.postAtFrontOfQueue(mOnEnd);
}
}
==>是否设置了事件监听
private boolean hasAnimationListener() {
return mListener != null;
}
/**
* <p>Binds an animation listener to this animation. The animation listener
* is notified of animation events such as the end of the animation or the
* repetition of the animation.</p>
*
* @param listener the animation listener to be notified
*/
public void setAnimationListener(AnimationListener listener) {
mListener = listener;
}
==>设置mListenerHandler
/**
* Sets the handler used to invoke listeners.
*
* @hide
*/
public void setListenerHandler(Handler handler) {
if (mListenerHandler == null) {
mOnStart = new Runnable() {
public void run() {
dispatchAnimationStart();
}
};
mOnRepeat = new Runnable() {
public void run() {
dispatchAnimationRepeat();
}
};
mOnEnd = new Runnable() {
public void run() {
dispatchAnimationEnd();
}
};
}
mListenerHandler = handler;
}
③
==>Start() Repeat() End() mListener==AnimationListener
void dispatchAnimationStart() {
if (mListener != null) {
mListener.onAnimationStart(this);
}
}
void dispatchAnimationRepeat() {
if (mListener != null) {
mListener.onAnimationRepeat(this);
}
}
void dispatchAnimationEnd() {
if (mListener != null) {
mListener.onAnimationEnd(this);
}
}
==>Start() Repeat() End() 接口
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
public static interface AnimationListener {
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(Animation animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animation animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(Animation animation);
}
④
/**
* Used to indicate that the parent of this view should be invalidated. This functionality
* is used to force the parent to rebuild its display list (when hardware-accelerated),
* which is necessary when various parent-managed properties of the view change, such as
* alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method will propagate
* an invalidation event to the parent.
*
* @hide
*/
@UnsupportedAppUsage
protected void invalidateParentIfNeeded() {
if (isHardwareAccelerated() && mParent instanceof View) {
((View) mParent).invalidate(true); ===>⑤
}
}
⑤
==>
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* @param invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View's contents or
* dimensions have not changed.
* @hide
*/
@UnsupportedAppUsage
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); ===>⑥
}
mTop:视图相对父容器坐标系的上侧边界坐标。
mLeft:视图相对父容器坐标系的左侧边界坐标。
mRight-mLeft:视图宽度。
mBottom-mTop:视图高度。
⑥
==>
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) { ===>⑦
return;
}
// Reset content capture caches
mCachedContentCaptureSession = null;
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
}
}
⑦跳过绘制
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
public static final int VISIBLE = 0x00000000;
public static final int INVISIBLE = 0x00000004;
public static final int GONE = 0x00000008;
static final int VISIBILITY_MASK = 0x0000000C;
==>VISIBLE + INVISIBLE + GONE = VISIBILITY_MASK
5. public static long currentAnimationTimeMillis() {
AnimationState state = sAnimationState.get();
if (state.animationClockLocked) {
// It's important that time never rewinds
return Math.max(state.currentVsyncTimeMillis,
state.lastReportedTimeMillis);
}
state.lastReportedTimeMillis = SystemClock.uptimeMillis();
return state.lastReportedTimeMillis;
}
6. /**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
* otherwise.
*
* @param currentTime Where we are in the animation. This is wall clock time.
* @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @return True if the animation is still running
*/
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {
mStartTime = currentTime;
}
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
// time is a step-change with a zero duration
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
final boolean expired = normalizedTime >= 1.0f || isCanceled();
mMore = !expired;
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
if (!mStarted) {
fireAnimationStart();
mStarted = true;
if (NoImagePreloadHolder.USE_CLOSEGUARD) {
guard.open("cancel or detach or getTransformation");
}
}
if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if (mCycleFlip) {
normalizedTime = 1.0f - normalizedTime;
}
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);
}
if (expired) {
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
fireAnimationRepeat();
}
}
if (!mMore && mOneMoreTime) {
mOneMoreTime = false;
return true;
}
return mMore;
}
就只看到此处了,没有更深入的研究了.QWQ