android 帧动画工具类,Android 动画框架的学习

Android动画可作用于View/ViewGroup,Actvity,Fragment实现炫酷的交互效果。经过几天的探究,搞清楚了各类动画的使用和动画的实现原理,在此记录以下。

尽管Android动画有好几种类别,但是各种动画的实现核心都是TimeInterpolator->Interpolator->各种Interpolator。大致过程是通过Interpolator计算出时间相关的input,通过这个input计算出各类fraction,利用各类Interpolator计算出的fraction计算出各种状态参数(时间相关),将这些参数使用到动画效果上,Android会通过一定的机制不断重复这个过程(16ms为周期)就构成了我们看到的动画。

从动画的分类上有以下几种类别:

1、FrameAnimation

FrameAnimation顾名思义就是帧动画,通过逐帧播放来实现的动画。Frame Animation可以通过xml来实现,也可应用代码来实现:

xml实现时根结点必须是,根结点内包含多个元素,例如:

android:oneshot=["true" | "false"] >

android:drawable="@drawable/frame1"

android:duration="250" />

android:drawable="@drawable/frame2"

android:duration="250" />

android:drawable="@drawable/frame3"

android:duration="250" />

android:drawable="@drawable/frame4"

android:duration="250" />

对于这种动画,就是不断的更换显示的Drawable来实现动态效果。

2、TweenAnimation

可以对View进行一系列的变换,如平移,翻转,缩放,淡入淡出,也可以将他们组合起来形成混合的动画效果。TweenAnimation的实现方式也有两种,分别可以用代码和xml实现。

xml:

android:interpolator="@[package:]anim/interpolator_resource"

android:shareInterpolator=["true" | "false"] >

android:fromAlpha="float"

android:toAlpha="float" />

android:fromXScale="float"

android:toXScale="float"

android:fromYScale="float"

android:toYScale="float"

android:pivotX="float"

android:pivotY="float" />

android:fromXDelta="float"

android:toXDelta="float"

android:fromYDelta="float"

android:toYDelta="float" />

android:fromDegrees="float"

android:toDegrees="float"

android:pivotX="float"

android:pivotY="float" />

...

加载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.

*

* The callback runs once then is automatically removed.

*

*

* @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.

*

* 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.

*

* 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.

*

*

*

To post an animation to be processed on a regular time basis synchronized with

* display frame rendering, use {@link android.animation.ValueAnimator#start}.

*

To post a {@link Runnable} to be invoked once at the beginning of the next display

* frame, use {@link View#postOnAnimation}.

*

To post a {@link Runnable} to be invoked once at the beginning of the next display

* frame after a delay, use {@link View#postOnAnimationDelayed}.

*

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)}.

*

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.

*

*

* 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.

*

*

*

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}.

*

... and that's about it.

*

*

* 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.

*

*/

有一句话引起了我的注意:

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???

有待探究。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值