共计 13681 个字符,预计需要花费 35 分钟才能阅读完成。
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.jianshu.com/p/435…,如有侵权,请分割删除。
相干视频:
【Android 根底课程】风行框架之热修复解读_哔哩哔哩_bilibili
【Android 根底课程】高阶 UI 进阶, 从零到精通 (二)_哔哩哔哩_bilibili
【Android 进阶零碎学习——面对层出不穷的第三方 SDK,身为架构师该怎么做?_哔哩哔哩_bilibili
教你疾速成长为一位具备外围竞争力的挪动架师_哔哩哔哩_bilibili
Android 架构师核心技术——申请型框架通用设计方案_哔哩哔哩_bilibili