从源码的角度分析Android动画运行原理
来源:互联网 发布:数控车编程软件 编辑:程序博客网 时间:2024/05/16 10:53
一.概述
首先说说动画的运行模式,一共有两种模式:
- 独占模式,即程序主线程进入一个循环,根据动画指令不断刷新屏幕,直到动画结束;
- 中断模式,即有单独一个线程对时间计数,每隔一定的时间向主线程发通知,主线程接到通知后更新屏幕;
接下来我们看看动画的运行原理,一般情况下我们会调用使用如下的方式开启动画:
AlphaAnimation animation = new AlphaAnimation(0,1f); animation.setDuration(200); animation.setFillAfter(true); button.startAnimation(animation);
重点在startAnimation这个方法,这个方法是在哪定义的呢?是在所有控件的父类,View中,所以我们去View的源码中看看。
二.源码分析
public void startAnimation(Animation animation) { animation.setStartTime(Animation.START_ON_FIRST_FRAME); setAnimation(animation); invalidateParentCaches(); invalidate(true); }
首先给animation设置了一个起始时间START_ON_FIRST_FRAME,这个值是Animation类中定义的一个常量,值为-1,然后调用View中的setAnimation传入我们的动画对象,
public void setAnimation(Animation animation) { mCurrentAnimation = animation; if (animation != null) { // If the screen is off assume the animation start time is now instead of // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time // would cause the animation to start when the screen turns back on if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) { animation.setStartTime(AnimationUtils.currentAnimationTimeMillis()); } animation.reset(); } }
在setAnimation方法里面,我们的animation对象会赋值给mCurrentAnimation这样一个全局变量,然后进行了判断,满足条件的话又重新设置了动画的时间,
public static long currentAnimationTimeMillis() { return SystemClock.uptimeMillis(); }
这里返回了一个系统时间。然后调用reset方法重置动画为初始状态。
也就是说setAnimation主要做了两件事。第一件事就是设置动画起始时间,第二件事是重置动画状态。
最后调用invalidata方法重绘自己。
既然调用了setAnimation设置动画,那么应该有getAnimation来获取动画吧,最后我们在View的draw(Canvas canvas, ViewGroup parent, long drawingTime)里面发现了getAnimation这个方法。
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();
public Animation getAnimation() { return mCurrentAnimation; }
这个getAnimation方法返回的正是我们定义的动画对象,因为我们之前在setAniamation方法中,将动画对象赋值给了这个全局变量。
接下来有调用了applyLegacyAnimation这个方法,
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired) { Transformation invalidationTransform; final int flags = parent.mGroupFlags; final boolean initialized = a.isInitialized(); if (!initialized) { a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight()); a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop); if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler); onAnimationStart(); } final Transformation t = parent.getChildTransformation(); 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; }
我们只看重点代码,这里调用了动画对象的getTransformation方法
public boolean getTransformation(long currentTime, Transformation outTransformation, float scale) { mScaleFactor = scale; return getTransformation(currentTime, outTransformation); }
该方法直接调用了两个参数的方法
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; 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 (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); }
该方法首先将当前时间currentTime处理成一个float类型的代表当前动画进度的对象。
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); applyTransformation(interpolatedTime, outTransformation);
然后将我们计算好的时间传入getInterpolation方法里面,获得一个插值器对应的时间,最后把这个时间和Transformation对象传入到applyTransformation方法中。
protected void applyTransformation(float interpolatedTime, Transformation t) { }
我们发现applyTransformation这个方法的实现是空的,为何呢?因为不同的子类对这个方法进行了不同的实现。接下来我们看看
AlphaAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) { final float alpha = mFromAlpha; t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime)); }
ScaleAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) { float sx = 1.0f; float sy = 1.0f; float scale = getScaleFactor(); if (mFromX != 1.0f || mToX != 1.0f) { sx = mFromX + ((mToX - mFromX) * interpolatedTime); } if (mFromY != 1.0f || mToY != 1.0f) { sy = mFromY + ((mToY - mFromY) * interpolatedTime); } if (mPivotX == 0 && mPivotY == 0) { t.getMatrix().setScale(sx, sy); } else { t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY); } }
RotateAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) { float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime); float scale = getScaleFactor(); if (mPivotX == 0.0f && mPivotY == 0.0f) { t.getMatrix().setRotate(degrees); } else { t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale); } }
TranslateAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) { float dx = mFromXDelta; float dy = mFromYDelta; if (mFromXDelta != mToXDelta) { dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime); } if (mFromYDelta != mToYDelta) { dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime); } t.getMatrix().setTranslate(dx, dy); }
可见applyTransformation()方法就是动画具体的实现,系统会以一个比较高的频率来调用这个方法,一般情况下60FPS,是一个非常流畅的画面了,也就是16ms,接下来我们验证一下这个时间。
public void click(View vIew){ Animation animation = new Animation() { private long last = 0; @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); long curr = System.currentTimeMillis(); long time = curr-last; last = curr; System.out.println("===time: "+time); t.getMatrix().postTranslate(10, 10); } }; animation.setDuration(500); textView.startAnimation(animation); }
可以看到时间间隔大概在20ms左右
- 从源码的角度分析Android动画运行原理
- android动画之从源码角度分析动画原理
- android动画之从源码角度分析动画原理
- Android属性动画,从源码的角度分析
- android动画之从源码角度分析动画原理(一)
- android动画之从源码角度分析动画原理(二)
- 从源码角度来剖析Rxjava的运行原理
- 从源码角度分析Android系统的异常捕获机制是如何运行的
- 从源码角度理解android动画Interpolator类的使用
- 从源码角度理解android动画Interpolator类的使用
- 从源码角度分析RACObserve的实现原理(一)
- 从源码角度一步步分析AsyncTask的用法与原理
- 从源码角度分析postDelayed原理
- 【android】从源码的角度深入分析Scroller
- 从源码角度分析Android中的Binder机制的前因后果
- 从源码的角度分析Android消息处理机制
- 简单从Android源码的角度分析下SharePreference
- 从源码角度分析Android Context 对象
- 通过数据得到拟合数据并制图
- 需求评审和分析
- 通过cookie保存并读取用户登录信息实例
- 对大小端模式的学习
- 非IDE方式手动配置struts2环境之hello_world
- 从源码的角度分析Android动画运行原理
- Java问题总结之32-内存释放
- iOS 获取客户端ip
- js传值到action出现中文乱码问题
- 201603242031
- poj2411(轮廓线动态规划)
- bzoj1588 [HNOI2002]营业额统计 裸splay
- HDU 2553 : N皇后问题
- 一点点小随笔:min-width