💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# Drawable Animation ## 帧动画使用 ### 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类使用 ``` ImageView image = (ImageView) findViewById(R.id.image); AnimationDrawable animationDrawable = (AnimationDrawable) image.getDrawable(); animationDrawable.start(); ``` ## DrawableAnimation流程图 ![](../images/DrawableAnimation流程图.png) ## 帧动画代码执行过程 ### 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); } } //设置当前展示第几帧 private void setFrame(int frame, boolean unschedule, boolean animate) { if (frame >= mAnimationState.getChildCount()) { return; } mAnimating = animate; mCurFrame = frame; selectDrawable(frame); //如果取消下一帧任务,或者这已经是当前最后一帧,则取消当帧动画任务 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]); } } //安排动画绘制任务 public void scheduleSelf(Runnable what, long when) { //该Callback是当前AnimationDrawable绑定的View final Callback callback = getCallback(); //判断当前绑定的View是否被销毁 if (callback != null) { callback.scheduleDrawable(this, what, when); } } } ``` ### scheduleDrawable - [ViewRootImpl的独白,我不是一个View(布局篇)](https://blog.csdn.net/stven_king/article/details/78775166) - [Android系统的编舞者Choreographer](https://blog.csdn.net/stven_king/article/details/80098845) ``` 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) { //请求Vsync信号同步 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同步回调 @Override public void run() { nextFrame(false); } //继续执行下一帧动画 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; } //新一轮的循环又开始 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绑定当前的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); } } ``` ### 内存方面 帧动画相比较属性动画而言可能会出现OOM,因为在家的每一帧的图片会占用很大的内存空间。 帧动画不会出现内存泄露的问题: ``` public abstract class Drawable { /***部分代码省略***/ //持有当前View的弱引用,当View回收之后,没办法继续下一帧的展示 private WeakReference<Callback> mCallback = null; public Callback getCallback() { if (mCallback != null) { return mCallback.get(); } return null; } } ```