Android动画可作用于View/ViewGroup,Actvity,Fragment实现炫酷的交互成果。通过几天的探索,搞清楚了各类动画的应用和动画的实现原理,在此记录以下。
只管Android动画有好几种类别,然而各种动画的实现外围都是TimeInterpolator->Interpolator->各种Interpolator。大抵过程是通过Interpolator计算出工夫相干的input,通过这个input计算出各类fraction,利用各类Interpolator计算出的fraction计算出各种状态参数(工夫相干),将这些参数应用到动画成果上,Android会通过肯定的机制一直反复这个过程(16ms为周期)就形成了咱们看到的动画。
从动画的分类上有以下几种类别:
1、FrameAnimation
FrameAnimation顾名思义就是帧动画,通过逐帧播放来实现的动画。Frame Animation能够通过xml来实现,也可利用代码来实现:
xml实现时根结点必须是<animation-list>,根结点内蕴含多个<item>元素,例如:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot=["true" | "false"] > <item android:drawable="@drawable/frame1" android:duration="250" /> <item android:drawable="@drawable/frame2" android:duration="250" /> <item android:drawable="@drawable/frame3" android:duration="250" /> <item android:drawable="@drawable/frame4" android:duration="250" /> </animation-list>
对于这种动画,就是一直的更换显示的Drawable来实现动态效果。
2、TweenAnimation
能够对View进行一系列的变换,如平移,翻转,缩放,淡入淡出,也能够将他们组合起来造成混合的动画成果。TweenAnimation的实现形式也有两种,别离能够用代码和xml实现。
xml:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@[package:]anim/interpolator_resource" android:shareInterpolator=["true" | "false"] > <alpha android:fromAlpha="float" android:toAlpha="float" /> <scale android:fromXScale="float" android:toXScale="float" android:fromYScale="float" android:toYScale="float" android:pivotX="float" android:pivotY="float" /> <translate android:fromXDelta="float" android:toXDelta="float" android:fromYDelta="float" android:toYDelta="float" /> <rotate android:fromDegrees="float" android:toDegrees="float" android:pivotX="float" android:pivotY="float" /> <set> ... </set> </set>
加载xml动画能够用Android SDK提供的工具类:
AnimationUtils.loadAnimations();
至于代码实现形式:
//提供了以下几种Animation AlphaAnimation TranslateAnimation ScaleAnimation RotateAnimation AnimationSet.addAnimation(Animation) //应用办法根本与定义xml统一
Animation的运作依赖两个办法:
/** * 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; } protected void applyTransformation(float interpolatedTime, Transformation t) { }
每次调用getTransformation会计算一个normalizedTime,这个normalizedTime会作为interpolator的input传到interpolator中:
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
失去一个通过inpterpolator计算过的fraction(interpolatedTime)。之后以这个interpolatedTime为参数回调applyTransformation:
applyTransformation(interpolatedTime, outTransformation);
applyTransformation的另一参数,outTransformation是一个Transformation对象,其中的两个成员变量如下:
protected Matrix mMatrix; protected float mAlpha;
也就是说通过这个outTransformation,能够对它的alpha或者matrix进行运算,而后Android会读取通过Animation运算的outTransformation里的这两个变量而后作用于要实现动画成果的组件View/ViewGroup,Actvity,Fragment上实现动画成果。至于如何实现这个过程,上面会有剖析。
通过下面的剖析得悉,Animation的运行依赖Android自身的机制回调(每帧都得回调getTransformation和applyTransformation)无奈本身进行运算计算fraction,并且可参加运算的只有Transformation对象里的alpha和matrix,所以Animation只能实现简略的Alpha,Scale,Translate,Rotate变换成果。
3、PropertyAnimator
Android提供了3种,别离是:
ObjectAnimator TimeAnimator ValueAnimator
下面剖析的Animation受限于Android自身的回调,只能实现Alpha,Scale,Translate,Rotate的变换。而Animator没有此限度,它不依赖于Android自身的机制回调,然而它意依赖于Looper的Thread,上源码:
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; // Special case: reversing from seek-to-0 should act as if not seeked at all. if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) { if (mRepeatCount == INFINITE) { // Calculate the fraction of the current iteration. float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction)); mSeekFraction = 1 - fraction; } else { mSeekFraction = 1 + mRepeatCount - mSeekFraction; } } mStarted = true; mPaused = false; mRunning = false; mAnimationEndRequested = false; // 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); } } }
从start开始剖析,从代码可知只能在Looper Thread上开启Animator,一看到Looper咱们就霎时豁然开朗,原来Animator的实现也离不卡Handler机制。
AnimationHandler animationHandler = AnimationHandler.getInstance(); animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
Animator取得一个AnimationHandler实例,并把本身作为回调传给这个AnimationHandler:
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback { ... } public class AnimationHandler { ... /** * Callbacks that receives notifications for animation timing and frame commit timing. */ interface AnimationFrameCallback { /** * Run animation based on the frame time. * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time * base. */ void doAnimationFrame(long frameTime); /** * This notifies the callback of frame commit time. Frame commit time is the time after * traversals happen, as opposed to the normal animation frame time that is before * traversals. This is used to compensate expensive traversals that happen as the * animation starts. When traversals take a long time to complete, the rendering of the * initial frame will be delayed (by a long time). But since the startTime of the * animation is set before the traversal, by the time of next frame, a lot of time would * have passed since startTime was set, the animation will consequently skip a few frames * to respect the new frameTime. By having the commit time, we can adjust the start time to * when the first frame was drawn (after any expensive traversals) so that no frames * will be skipped. * * @param frameTime The frame time after traversals happen, if any, in the * {@link SystemClock#uptimeMillis()} time base. */ void commitAnimationFrame(long frameTime); } }
然而咱们发现。。。特么的这个AnimationHandler基本就不是一个Handler。
从新回到Animator的执行流程上。。。
在start函数里咱们看到有一个:
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
跟进去:
/** * Register to get a callback on the next frame after the delay. */ 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)); } }
发现有这样一句话:
getProvider().postFrameCallback(mFrameCallback);
Provider又是什么呢?
private AnimationFrameCallbackProvider getProvider() { if (mProvider == null) { mProvider = new MyFrameCallbackProvider(); } return mProvider; }
MyFrameCallbackProvider又是什么呢?
/** * Default provider of timing pulse that uses Choreographer for frame callbacks. */ private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider { final Choreographer mChoreographer = Choreographer.getInstance(); @Override public void postFrameCallback(Choreographer.FrameCallback callback) { mChoreographer.postFrameCallback(callback); } @Override public void postCommitCallback(Runnable runnable) { mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null); } @Override public long getFrameTime() { return mChoreographer.getFrameTime(); } @Override public long getFrameDelay() { return Choreographer.getFrameDelay(); } @Override public void setFrameDelay(long delay) { Choreographer.setFrameDelay(delay); } }
Choreographer又是什么呢?
public final class Choreographer { ... /** * Posts a frame callback to run on the next frame. * <p> * The callback runs once then is automatically removed. * </p> * * @param callback The frame callback to run during the next frame. * * @see #postFrameCallbackDelayed * @see #removeFrameCallback */ public void postFrameCallback(FrameCallback callback) { postFrameCallbackDelayed(callback, 0); } public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN, delayMillis); } private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis); } synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { scheduleFrameLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } }
看到这里。。。终于现出原形了呈现了一个mHandler,mHandler又是什么呢?
public final class Choreographer { private final FrameHandler mHandler; ... } 它是Choreographer的一个成员变量,从命名上看仿佛是一个与Frame(帧)相干的Handler: Java private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break; } } }
然而还有一点:
getProvider().postFrameCallback(mFrameCallback);
这个mFrameCallback对应的类是:
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); if (mAnimationCallbacks.size() > 0) { getProvider().postFrameCallback(this); } } };
这又是一系列简单的callback,剖析明确了也写不明确,但FrameHandler的doFrame最终会调用咱们的mFrameCallback,也就是会调用到doAnimationFrame,最终会调用到Animator的animateValue办法。再之后大家都晓得了。。。会调用AnimatorUpdateListener的onAnimationUpdate。一顿剖析,大抵过程明确了,然而这个机制也太简单了,牵扯了太多的方面,太多的类。什么时候本人能有这样的设计能力啊。。
对于Choreographer这个类:
*https://www.cnblogs.com/kross/p/4087780.html
这篇文件有比拟具体的剖析,临时还不能了解那么多。
其它的
对于Chroeographer源码正文是这样写的:
/** * Coordinates the timing of animations, input and drawing. * <p> * The choreographer receives timing pulses (such as vertical synchronization) * from the display subsystem then schedules work to occur as part of rendering * the next display frame. * </p><p> * Applications typically interact with the choreographer indirectly using * higher level abstractions in the animation framework or the view hierarchy. * Here are some examples of things you can do using the higher-level APIs. * </p> * <ul> * <li>To post an animation to be processed on a regular time basis synchronized with * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li> * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display * frame, use {@link View#postOnAnimation}.</li> * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display * frame after a delay, use {@link View#postOnAnimationDelayed}.</li> * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the * next display frame, use {@link View#postInvalidateOnAnimation()} or * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li> * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in * sync with display frame rendering, do nothing. This already happens automatically. * {@link View#onDraw} will be called at the appropriate time.</li> * </ul> * <p> * However, there are a few cases where you might want to use the functions of the * choreographer directly in your application. Here are some examples. * </p> * <ul> * <li>If your application does its rendering in a different thread, possibly using GL, * or does not use the animation framework or view hierarchy at all * and you want to ensure that it is appropriately synchronized with the display, then use * {@link Choreographer#postFrameCallback}.</li> * <li>... and that's about it.</li> * </ul> * <p> * Each {@link Looper} thread has its own choreographer. Other threads can * post callbacks to run on the choreographer but they will run on the {@link Looper} * to which the choreographer belongs. * </p> */
有一句话引起了我的留神:
If your application does its rendering in a different thread, possibly using GL,
- or does not use the animation framework or view hierarchy at all
也就是说,通常咱们的View只能在创立它的线程内进行事件处理,动画,或者绘制的起因在于,这些框架和机制的实现都依赖于这个类,这个类是线程相干。若要在其余线程内渲染能够间接应用Choreographer#postFrameCallback???
有待探索。。
文章转自 https://www.gaodaima.com/p/435… ,如有侵权,请分割删除。
相干视频:
【Android 根底课程】风行框架之热修复解读_哔哩哔哩_bilibili
【Android 根底课程】高阶UI进阶, 从零到精通(二)_哔哩哔哩_bilibili
【Android进阶零碎学习——面对层出不穷的第三方SDK,身为架构师该怎么做?_哔哩哔哩_bilibili
教你疾速成长为一位具备外围竞争力的挪动架师_哔哩哔哩_bilibili
Android架构师核心技术——申请型框架通用设计方案_哔哩哔哩_bilibili