前言
Android 平臺(tái)提供了三類(lèi)動(dòng)畫(huà)攻泼,一類(lèi)是 Tween 動(dòng)畫(huà)-Animation胖秒,即通過(guò)對(duì)場(chǎng)景里的對(duì)象不斷做圖像變換 ( 平移雷厂、縮放凤藏、旋轉(zhuǎn) ) 產(chǎn)生動(dòng)畫(huà)效果奸忽;第二類(lèi)是 Frame 動(dòng)畫(huà),即順序播放事先做好的圖像揖庄,跟電影類(lèi)似栗菜。最后一種就是3.0之后才出現(xiàn)的屬性動(dòng)畫(huà)PropertyAnimator(在下文我們講幀動(dòng)畫(huà)和補(bǔ)間動(dòng)畫(huà)統(tǒng)一稱為View動(dòng)畫(huà))。如果有人對(duì)ViewGroup內(nèi)部View使用過(guò)View動(dòng)畫(huà)的還知道有l(wèi)ayout-animation蹄梢。
大家對(duì)這三種動(dòng)畫(huà)基本都能熟練的使用疙筹,那么…...?
- 想知道動(dòng)畫(huà)與界面渲染與屏幕刷新有著什么樣的關(guān)系担忧?
- 想知道屬性動(dòng)畫(huà)為什么會(huì)發(fā)生內(nèi)存泄露么?
因?yàn)楸疚恼轮袝?huì)有一些屏幕刷新赴肚、Vsync信號(hào)相關(guān)的知識(shí)點(diǎn)避除,讀過(guò)我寫(xiě)的 Android的16ms和垂直同步以及三重緩存 和Android系統(tǒng)的編舞者Choreographer 這兩篇文章的同學(xué)會(huì)可能會(huì)更容易了解本文章。
接下來(lái)拿起我們的鍵盤(pán)翘盖、鼠標(biāo)和顯示器桂塞,我們將探索從Android源碼(android-23)的角度去探索動(dòng)畫(huà)的實(shí)現(xiàn)~!
動(dòng)畫(huà)的介紹
Drawable Animation
也就是所謂的幀動(dòng)畫(huà)馍驯,F(xiàn)rame動(dòng)畫(huà)阁危。指通過(guò)指定每一幀的圖片和播放時(shí)間,有序的進(jìn)行播放而形成動(dòng)畫(huà)效果汰瘫。
Tween Animation
視圖動(dòng)畫(huà)狂打,也就是所謂補(bǔ)間動(dòng)畫(huà),Tween動(dòng)畫(huà)混弥。指通過(guò)指定View的初始狀態(tài)趴乡、變化時(shí)間、方式蝗拿,通過(guò)一系列的算法去進(jìn)行圖形變換晾捏,從而形成動(dòng)畫(huà)效果,主要有Alpha哀托、Scale仓手、Translate、Rotate四種效果嗽冒。注意:只是在視圖層實(shí)現(xiàn)了動(dòng)畫(huà)效果,并沒(méi)有真正改變View的屬性辛慰。
Property Animation
屬性動(dòng)畫(huà),通過(guò)不斷的改變View的屬性,不斷的重繪而形成動(dòng)畫(huà)效果帅腌。相比于視圖動(dòng)畫(huà)驰弄,View的屬性是真正改變了戚篙。注意:Android 3.0(API 11)以上才支持岔擂。
接下來(lái)我們按照倒敘來(lái)揭開(kāi)一個(gè)一個(gè)動(dòng)畫(huà)的神秘面紗_。
Property Animation
屬性動(dòng)畫(huà)的優(yōu)點(diǎn)
- 屬性動(dòng)畫(huà)顧名思義就是改變了View的屬性塑崖,而不僅僅是繪制的位置规婆。
- 屬性動(dòng)畫(huà)可以操作的屬性相比于補(bǔ)間動(dòng)畫(huà)大大增加抒蚜,除了常用的平移嗡髓、旋轉(zhuǎn)饿这、縮放撞秋、透明度還有顏色等,基本上能通過(guò)View.setXX來(lái)設(shè)置的屬性,屬性動(dòng)畫(huà)都可以操作,這大大增加了我們?cè)谑褂脛?dòng)畫(huà)時(shí)的靈活性。
- 屬性動(dòng)畫(huà)分為ObjectAnimator和ValueAnimator,其中ObjectAnimator是繼承于ValueAnimator廓八。
ValueAnimator
ValueAnimator并不會(huì)改變屬性的大小剧蹂,他只是在一段時(shí)間生成某些值烦却。我們需要做的是監(jiān)聽(tīng)這些值得改變從而該改變View的屬性其爵,進(jìn)而產(chǎn)生動(dòng)畫(huà)效果摩渺。
下邊的動(dòng)畫(huà)就是對(duì)mView進(jìn)行平移:
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的基礎(chǔ)之上摇幻,對(duì)控件的某個(gè)屬性執(zhí)行一次動(dòng)畫(huà)。
相同的對(duì)mView進(jìn)行平移的動(dòng)畫(huà)ObjectAnimator是這樣實(shí)現(xiàn)的:
ObjectAnimator animator=ObjectAnimator.ofFloat (mView,"translationX",0,1000);
animator.setDuration (1000);
animator.start ();
PropertyAnimation流程圖
屬性動(dòng)畫(huà)代碼的執(zhí)行過(guò)程
start
ObjectAnimator.start
public final class ObjectAnimator extends ValueAnimator {
/***部分代碼省略***/
@Override
public void start() {
//首先依次判斷了當(dāng)前動(dòng)畫(huà)、等待的動(dòng)畫(huà)憨栽、延遲的動(dòng)畫(huà)中是否有和當(dāng)前動(dòng)畫(huà)相同的動(dòng)畫(huà)
//若有就把相同的動(dòng)畫(huà)取消掉
// 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();
}
}
}
/***部分代碼省略***/
}
/***部分代碼省略***/
//然后調(diào)用ValueAnimator.start()方法
super.start();
}
}
ValueAnimator.start
public class ValueAnimator extends Animator {
/***部分代碼省略***/
protected static ThreadLocal<AnimationHandler> sAnimationHandler =
new ThreadLocal<AnimationHandler>();
//保證每個(gè)線程有且只有一個(gè)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");
}
/***部分代碼省略***/
//創(chuàng)建或者獲取animationHandler實(shí)例
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;
//回調(diào)監(jiān)聽(tīng)器,通知?jiǎng)赢?huà)開(kāi)始
notifyStartListeners();
}
//開(kāi)始動(dòng)畫(huà)
animationHandler.start();
}
//回調(diào)監(jiān)聽(tīng)器锯蛀,通知?jiǎng)赢?huà)開(kāi)始
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) {
//初始化動(dòng)畫(huà)
initAnimation();
if (fraction < 0) {
fraction = 0;
}
/***部分代碼省略***/
}
}
AnimationHandler.start
public class ValueAnimator extends Animator {
/***部分代碼省略***/
protected static class AnimationHandler implements Runnable {
/***部分代碼省略***/
//開(kāi)始動(dòng)畫(huà)
public void start() {
scheduleAnimation();
}
//發(fā)送VSYNC信號(hào)回調(diào)請(qǐng)求
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mAnimationScheduled = true;
}
}
// Called by the Choreographer.
//Choreographer的VSYNC信號(hào)回調(diào)
@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);
//執(zhí)行動(dòng)畫(huà)
//doAnimationFrame方法返回ture,則該動(dòng)畫(huà)添加在mEndingAnims隊(duì)列中進(jìn)行end操作
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
/***部分代碼省略***/
//循環(huán)執(zhí)行,直到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用于以后反射執(zhí)行g(shù)et、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 {
//執(zhí)行完這次,該動(dòng)畫(huà)結(jié)束
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
//設(shè)置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就是計(jì)算每幀動(dòng)畫(huà)所對(duì)應(yīng)的值
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
//屬性值得改變的回調(diào)
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) {
//設(shè)置target的屬性值注祖,進(jìn)行View的移動(dòng)是晨,產(chǎn)生動(dòng)畫(huà)
mValues[i].setAnimatedValue(target);
}
}
}
PropertyValuesHolder
PropertyValuesHolder這個(gè)類(lèi)的意義就是罩缴,它其中保存了動(dòng)畫(huà)過(guò)程中所需要操作的屬性和對(duì)應(yīng)的值箫章。我們通過(guò)ofFloat(Object target, String propertyName, float… values)構(gòu)造的動(dòng)畫(huà)炉抒,ofFloat()的內(nèi)部實(shí)現(xiàn)其實(shí)就是將傳進(jìn)來(lái)的參數(shù)封裝成PropertyValuesHolder實(shí)例來(lái)保存動(dòng)畫(huà)狀態(tài)焰薄。在封裝成PropertyValuesHolder實(shí)例以后,后期的各種操作也是以PropertyValuesHolder為主的亩码。
ObjectAnimator.ofFloat
我們先看看我們之前的代碼中構(gòu)造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) {
//構(gòu)造ObjectAnimator
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
//設(shè)置屬性值
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);
}
}
}
構(gòu)造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的屬性鞭光,通過(guò)set惰许、get方法
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
屬性動(dòng)畫(huà)的內(nèi)存泄露
- 上面講述到
ValueAnimator.AnimationHandler.doAnimationFrame
的時(shí)候說(shuō)過(guò),這個(gè)方法會(huì)循環(huán)執(zhí)行佩伤。 - 因?yàn)?
ValueAnimator.AnimationHandler.doAnimationFrame
每次執(zhí)行完動(dòng)畫(huà)(如果動(dòng)畫(huà)沒(méi)有結(jié)束)生巡,都在再一次請(qǐng)求Vsync同步信號(hào)回調(diào)給自己孤荣。 -
Choreographer
的回調(diào)都配post進(jìn)入了當(dāng)前線程的looper隊(duì)列中垃环。 -
mRepeatCount
無(wú)窮大,會(huì)導(dǎo)致該循環(huán)會(huì)一直執(zhí)行下去劲赠,即使關(guān)閉當(dāng)前的頁(yè)面也不會(huì)停止秸谢。
Drawable Animation
幀動(dòng)畫(huà)使用
animalist.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@mipmap/c_1"
android:duration="50" />
<item
android:drawable="@mipmap/c_2"
android:duration="50" />
<!-- 省略... -->
<item
android:drawable="@mipmap/circle_19"
android:duration="50" />
<item
android:drawable="@mipmap/circle_20"
android:duration="50" />
</animation-list>
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ansen.frameanimation.sample.MainActivity">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/animlist" />
</LinearLayout>
java類(lèi)使用
ImageView image = (ImageView) findViewById(R.id.image);
AnimationDrawable animationDrawable = (AnimationDrawable) image.getDrawable();
animationDrawable.start();
DrawableAnimation流程圖
幀動(dòng)畫(huà)代碼執(zhí)行過(guò)程
start
public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
/***代碼部分省略***/
@Override
public void start() {
mAnimating = true;
if (!isRunning()) {
// Start from 0th frame.
setFrame(0, false, mAnimationState.getChildCount() > 1
|| !mAnimationState.mOneShot);
}
}
//設(shè)置當(dāng)前展示第幾幀
private void setFrame(int frame, boolean unschedule, boolean animate) {
if (frame >= mAnimationState.getChildCount()) {
return;
}
mAnimating = animate;
mCurFrame = frame;
selectDrawable(frame);
//如果取消下一幀任務(wù),或者這已經(jīng)是當(dāng)前最后一幀最铁,則取消當(dāng)幀動(dòng)畫(huà)任務(wù)
if (unschedule || animate) {
unscheduleSelf(this);
}
if (animate) {
// Unscheduling may have clobbered these values; restore them
mCurFrame = frame;
mRunning = true;
scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
}
}
//安排動(dòng)畫(huà)繪制任務(wù)
public void scheduleSelf(Runnable what, long when) {
//該Callback是當(dāng)前AnimationDrawable綁定的View
final Callback callback = getCallback();
//判斷當(dāng)前綁定的View是否被銷(xiāo)毀
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
}
scheduleDrawable
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
/***部分代碼省略***/
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
//請(qǐng)求Vsync信號(hào)同步
mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
Choreographer.CALLBACK_ANIMATION, what, who,
Choreographer.subtractFrameDelay(delay));
} else {
ViewRootImpl.getRunQueue().postDelayed(what, delay);
}
}
}
}
run
public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
/***代碼部分省略***/
//Choreographer的Vsync同步回調(diào)
@Override
public void run() {
nextFrame(false);
}
//繼續(xù)執(zhí)行下一幀動(dòng)畫(huà)
private void nextFrame(boolean unschedule) {
int nextFrame = mCurFrame + 1;
final int numFrames = mAnimationState.getChildCount();
final boolean isLastFrame = mAnimationState.mOneShot && nextFrame >= (numFrames - 1);
// Loop if necessary. One-shot animations should never hit this case.
if (!mAnimationState.mOneShot && nextFrame >= numFrames) {
nextFrame = 0;
}
//新一輪的循環(huán)又開(kāi)始
setFrame(nextFrame, unschedule, !isLastFrame);
}
}
其他
CallBack的綁定
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
/***部分代碼省略***/
@Deprecated
public void setBackgroundDrawable(Drawable background) {
/***部分代碼省略***/
//清除之前的背景
if (mBackground != null) {
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
}
if (background != null) {
/***部分代碼省略***/
//Drawable綁定當(dāng)前的View
background.setCallback(this);
if (background.isStateful()) {
background.setState(getDrawableState());
}
background.setVisible(getVisibility() == VISIBLE, false);
mBackground = background;
applyBackgroundTint();
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
requestLayout = true;
}
} else {
/***部分代碼省略***/
}
computeOpaqueFlags();
if (requestLayout) {
requestLayout();
}
mBackgroundSizeChanged = true;
invalidate(true);
}
}
內(nèi)存方面
幀動(dòng)畫(huà)相比較屬性動(dòng)畫(huà)而言可能會(huì)出現(xiàn)OOM,因?yàn)樵诩业拿恳粠膱D片會(huì)占用很大的內(nèi)存空間膊夹。
幀動(dòng)畫(huà)不會(huì)出現(xiàn)內(nèi)存泄露的問(wèn)題:
public abstract class Drawable {
/***部分代碼省略***/
//持有當(dāng)前View的弱引用放刨,當(dāng)View回收之后嘉栓,沒(méi)辦法繼續(xù)下一幀的展示
private WeakReference<Callback> mCallback = null;
public Callback getCallback() {
if (mCallback != null) {
return mCallback.get();
}
return null;
}
}
Tween Animation
補(bǔ)間動(dòng)畫(huà)的使用
Animation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
translateAnimation.setDuration(500);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(true);//設(shè)置動(dòng)畫(huà)結(jié)束后保持當(dāng)前的位置(即不返回到動(dòng)畫(huà)開(kāi)始前的位置)
imageView.startAnimation(translateAnimation);
TweenAnimation流程圖
補(bǔ)間動(dòng)畫(huà)代碼的執(zhí)行過(guò)程
start
View:
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
//部分代碼省略
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) {
return;
}
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)) {
//部分代碼省略
// 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);
//執(zhí)行ViewParent的invalidateChild方法
p.invalidateChild(this, damage);
}
//部分代碼省略
}
}
}
ViewGroup
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
/***部分代碼省略***/
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
/***部分代碼省略***/
do {
/***部分代碼省略***/
//向頂部的View便利找到根View,即:ViewRootImpl
//執(zhí)行ViewRootImpl的invalidateChildInParent方法
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) (boundingRect.left - 0.5f),
(int) (boundingRect.top - 0.5f),
(int) (boundingRect.right + 0.5f),
(int) (boundingRect.bottom + 0.5f));
}
}
/***部分代碼省略***/
} while (parent != null);
}
}
}
ViewRootImpl
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/***部分代碼省略***/
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
/***部分代碼省略***/
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
/***部分代碼省略***/
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
//開(kāi)始View的繪制任務(wù)
scheduleTraversals();
}
}
}
之前寫(xiě)過(guò)一篇文章 ViewRootImpl的獨(dú)白,我不是一個(gè)View(布局篇) 其中 ViewRootImpl對(duì)mView進(jìn)行操作 講述了再ViewRootImpl
中View的繪制叉抡。
draw
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
//部分代碼省略
public void draw(Canvas canvas) {
/***部分代碼省略***/
//如果有子 View(DecorView當(dāng)然有子View)褥民,就會(huì)調(diào)用dispatchDraw() 將繪制事件通知給子 View洗搂。
//ViewGroup 重寫(xiě)了 dispatchDraw()耘拇,調(diào)用了 drawChild()
//drawChild() 調(diào)用了子 View 的 draw(Canvas, ViewGroup, long)
}
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
/***部分代碼省略***/
Transformation transformToApply = null;
boolean concatMatrix = false;
final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
final Animation a = getAnimation();
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
} else {
/***部分代碼省略***/
}
/***部分代碼省略***/
}
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
/***部分代碼省略***/
//繪制動(dòng)畫(huà)的當(dāng)前幀惫叛,并獲取當(dāng)前動(dòng)畫(huà)的狀態(tài)(是否繼續(xù)運(yùn)行)
boolean more = a.getTransformation(drawingTime, t, 1f);
if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
if (parent.mInvalidationTransformation == null) {
parent.mInvalidationTransformation = new Transformation();
}
invalidationTransform = parent.mInvalidationTransformation;
a.getTransformation(drawingTime, invalidationTransform, 1f);
} else {
invalidationTransform = t;
}
//如果動(dòng)畫(huà)沒(méi)有結(jié)果
if (more) {
if (!a.willChangeBounds()) {
if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
} else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
//進(jìn)行繪制
parent.invalidate(mLeft, mTop, mRight, mBottom);
}
} else {
/***部分代碼省略***/
//進(jìn)行繪制
parent.invalidate(left, top, left + (int) (region.width() + .5f),
top + (int) (region.height() + .5f));
}
}
return more;
}
}
running
public abstract class Animation implements Cloneable {
/***部分代碼省略***/
public boolean getTransformation(long currentTime, Transformation outTransformation) {
/***部分代碼省略***/
//執(zhí)行時(shí)間是否過(guò)期
final boolean expired = normalizedTime >= 1.0f;
mMore = !expired;
//動(dòng)畫(huà)進(jìn)度為0.0~1.0之間
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
/***部分代碼省略***/
//插值器計(jì)算動(dòng)畫(huà)執(zhí)行進(jìn)度
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
//真正的動(dòng)畫(huà)效果代碼執(zhí)行處(通過(guò)矩陣變化)
applyTransformation(interpolatedTime, outTransformation);
}
//如果動(dòng)畫(huà)繪制完成
if (expired) {
//判斷動(dòng)畫(huà)是否需要繼續(xù)循環(huán)
if (mRepeatCount == mRepeated) {
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;
}
}
其他
通過(guò)代碼分析可以證明補(bǔ)間動(dòng)畫(huà)也不會(huì)存在內(nèi)存泄露的問(wèn)題夸浅,因?yàn)樗强恐鳹iew的繪制來(lái)完成每一幀動(dòng)效的展示题篷。
使用動(dòng)畫(huà)的注意事項(xiàng)
OOM的問(wèn)題
這個(gè)問(wèn)題主要出現(xiàn)在幀動(dòng)畫(huà)中厅目,當(dāng)圖片數(shù)量過(guò)多的且圖片較大的時(shí)候就極易出現(xiàn)OOM损敷,這個(gè)在實(shí)際的開(kāi)發(fā)中要尤其注意拗馒,盡量避免使用幀動(dòng)畫(huà)诱桂。
內(nèi)存泄漏的問(wèn)題
在屬性動(dòng)畫(huà)中有一類(lèi)無(wú)限循環(huán)的動(dòng)畫(huà),這類(lèi)動(dòng)畫(huà)需要在Activity退出時(shí)及時(shí)停止友绝,否則導(dǎo)致Activity無(wú)法釋放從而造成內(nèi)存泄露迁客,通過(guò)驗(yàn)證發(fā)現(xiàn)View動(dòng)畫(huà)(幀動(dòng)畫(huà)和補(bǔ)間動(dòng)畫(huà))并不存在此問(wèn)題辞槐。
兼容性問(wèn)題
動(dòng)畫(huà)在3.0以下的系統(tǒng)上有兼容性問(wèn)題榄檬,在某些特殊場(chǎng)景可能無(wú)法正常工作鹿榜,因此要做好適配工作。
View動(dòng)畫(huà)的問(wèn)題
View動(dòng)畫(huà)對(duì)View的影像做動(dòng)畫(huà),并不是真正的改變View的狀態(tài)怀薛,因此有時(shí)候會(huì)出現(xiàn)動(dòng)畫(huà)完成后View無(wú)法影藏的現(xiàn)象枝恋,即
setVisibility(View.GONE)
失效了焚碌,這個(gè)時(shí)候只要調(diào)用view.clearAnimation()
清除View動(dòng)畫(huà)即可解決此問(wèn)題。
不要使用px
在進(jìn)行動(dòng)畫(huà)的過(guò)程中知押,要盡量使用dp台盯,使用px會(huì)導(dǎo)致在不同的設(shè)備上有不同的效果畏线。
動(dòng)畫(huà)元素的交互
將View移動(dòng)(平移)后寝殴,在Android3.0之前的系統(tǒng)上蚣常,不管是View動(dòng)畫(huà)還是屬性動(dòng)畫(huà)史隆,新位置均無(wú)法觸發(fā)單擊事件,同時(shí)老位置任然可以觸發(fā)單擊事件粘姜。盡管View已經(jīng)在視覺(jué)上不存在了孤紧,將View移回原位置以后号显,原位置的單擊事件繼續(xù)生效躺酒。從3.0開(kāi)始羹应,屬性動(dòng)畫(huà)的單擊事件觸發(fā)位置為移動(dòng)以后的位置,但View動(dòng)畫(huà)仍然在原位置雳刺。
硬件加速
使用動(dòng)畫(huà)的過(guò)程中掖桦,建議開(kāi)啟硬件加速枪汪,這樣會(huì)提交動(dòng)畫(huà)的流暢性料饥。
文章到這里就全部講述完啦,若有其他需要交流的可以留言哦~原叮!~奋隶!