三种基本动画
- Drawable Animation:指定每一帧的图片和播放时间,有序的进行播放而形成动画效果。
- View Animation:当View回溯到DecorView被标记为PFLAG_INVALIDATED,以致其将被重绘时,在View.draw()中,将动画某一帧的计算结果赋值给RenderNode/Canvas,从而改变绘制结果,连贯起来就形成了动画。
- Property Animation:在每次“Choreographer刷新“中,用差值器和估值器由时间比率计算出值,改变View的某项属性,从而形成动画效果。
差值器
差值器的作用是定义动画因子与时间比率(已过时长/总时长)的关系,根据时间比率算出动画因子供估值器使用。接口为TimeInterpolator。默认实现有:
- AccelerateDecelerateInterpolator:其止时变化较慢,而中间变化较快。
- AccelerateInterpolator:从慢到快。
- AnticipateInterpolator:
- AnticipateOvershootInterpolator:
- BounceInterpolator:
- CycleInterpolator:
- DecelerateInterpolator:
- LinearInterpolator:
- OvershootInterpolator:
估值器
估值器接收动画因子、起止值,计算出当前值,供改变属性。接口为TypeEvaluator。默认实现有:
- IntEvaluator:
- FloatEvaluator:
- ArgbEvaluator:
属性动画
重要的类
- KeyFrame:描述动画的某一帧的“时间/值”。mFraction:某一帧的动画因子。mValue:某一帧的值。
- mInterpolator:
- KeyFrameSet:一组KeyFrame。
- mEvaluator:计算关键帧之间帧的值所用的估值器。
- mInterpolator:只用于2关键帧帧的情况。
- PropertyValuesHolder:封装某属性及对应的KeyFrameSet。
- mEvaluator:计算关键帧之间帧的值所用的估值器,与KeyFrameSet中的是同一对象。
- ValueAnimator:只提供差值,需要手动修改对象的属性。
- mValues(PropertyValuesHolder[]):该动画中要改变的所有属性的PropertyValuesHolder。
- mValuesMap(HaskMap<String, PropertyValuesHolder>):该动画中要改变的所有属性及其对应的PropertyValuesHolder。
- mInterpolator:
- mPlayingState:
- mPaused:
- mResumed:
- AnimationHandler:不是真正的Handler。它递归地调用Choreographer.postCallback()向Choreographer中CALLBACK_ANIMATION队列插入Runnable。它被放入ThreadLocal以避免竞态条件(线程单例)。在执行过程中多次使用“集合克隆法”,以防止执行过程中增减集合元素造成的错误。
- mPendingAnimations:保存所有请求执行的动画。
- mAnimations:保存所有正在执行的动画。
- mDelayAnims:保存所有延迟执行的动画。
- mReadAnims:保存所有延迟执行的、已到执行时间的动画。
- mTmpAnimations:
- mEndAnims:
可见,KeyFrame、KeyFrameSet、ValueAnimator中都有差值器的引用。其中,KeyFrame、KeyFrameSet是同一个差值器对象,而ValueAnimator中是不同的。1帧属性动画要使用2个差值器,1个估值器。
ValueAnimator
创建动画
ValueAnimator.onInt()/onFloat()…
根据传入的参数创建至少2个(传入参数个)KeyFrame。第一个KeyFrame的mFraction为0,mValue为第一个参数;之后第i个KeyFrame的mFraction为i/传入参数数量,mValue为第i个参数。因此这些KeyFrame是均匀分布在时间轴上的。然后以这组KeyFrame为参数构建KeyFrameSet。再以KeyFrameSet为参数构建PropertyValuesHolder。再以PropertyValuesHolder为元素填充ValueAnimator.mValues及ValueAnimator.mValuesMap。
执行动画
ValueAnimator.start()
- 检查调用线程是否是Looper的;不是则抛出错误。
- 创建AnimationHandler,并把ValueAnimator对象加入AnimationHandler.mPendingAnimations,并调用AnimationHandler.start()。
- 在AnimationHandler.start()中,调用Choreographer.postCallback()向Choreographer中CALLBACK_ANIMATION队列插入Runnable。
- 当“Choreographer刷新“发生时,就会执行Runnable执行体。在执行体中,
- 遍历AnimationHandler.mPendingAnimations,
- 对立即执行的动画:调用ValueAnimator.startAnimation()。
- 对延迟执行的动画:加入AnimationHandler.mDelayAnims。
- 遍历AnimationHandler.mDelayAnims,把所有到时间了该执行的动画加入AnimationHandler.mReadAnims。
- 遍历AnimationHandler.mReadAnims,把其中的动画从AnimationHandler.mDelayAnims中删除,并把AnimationHandler.mAnimations中的元素都加入AnimationHandler.mTmpAnimations,并执行每个元素的ValueAnimator.doAnimationFrame()。
- 如果ValueAnimator.doAnimationFrame()返回true,说明这个动画执行完了,把它加入AnimationHandler.mEndAnims。
- 清空AnimationHandler.mTmpAnimations。
- 遍历AnimationHandler.mEndAnims,调用每个元素的ValueAnimator.endAnimation()。
- 清空AnimationHandler.mEndAnims。
- 调用Choreographer.postCallback()向Choreographer中CALLBACK_COMMIT队列插入Runnable,这个Runnable的作用是使AnimationHandler.mAnimations中的动画的开始时间与“Choreographer刷新”对齐。
- 如果此时AnimationHandler.mAnimations或AnimationHandler.mDelayAnims不为空,即还有动画需要执行,就再次调用Choreographer.postCallback()向Choreographer中CALLBACK_ANIMATION队列插入Runnable。这与3形成递归。
由以上知,ValueAnimator执行动画的关键在于3个方法:
startAnimation():遍历ValueAnimator.mValues,调用每个PropertyValuesHolder的init()。在init()中,创建估值器。然后把ValueAnimator对象加入AnimationHandler.mAnimations。
doAnimationFrame():先处理Pause/Resume/Stop状态,需要将时间对齐。然后计算时间比率。
- 时间比率 >= 1:处理“超时”/“反弹”等,转化为< 1的情况。
- 时间比率 <= 1:根据时间比率、由差值器计算出动画因子,再由估值器计算出值。
- 只有2个关键帧(准备阶段只传入了起止值):
- 时间比率 <= 0:
- 时间比率 == 1:
- 0 < 时间比率 < 1:
endAnimation():从集合中删除该元素。动画状态改为STOPPED。
另外,注意到Pause/Stop的动画,并不是删除了CALLBACK_ANIMATION队列中的Runnable,而是在doAnimationFrame()中不作出反应。
ObjectAnimator
ObjectAnimator继承自ViewAnimator,具有其所有特性,并添加了自动修改属性值的功能。
- 在创建阶段会把属性的Getter方法保存在mGetter(Method)、Setter方法保存在mSetter(Method)。
- 在执行阶段会用Setter方法为属性赋值。
View动画
重要的类
- Transformation:定义动画某一帧的变形信息。mMatrix(Matrix):变形矩阵。
- mAlpha:透明度。
- mTransformationType:变化类型。有4个值:TYPE_IDENTITY:没有变化。TYPE_ALPHA:透明度变化。TYPE_MATRIX:缩放、位移变化。TYPE_BOTH:透明度、缩放、位移变化。
- mClipRect:
- Animation:
- mPreviousRegion:保存相对于该View左上角的原本的四边位置。
- mListenerHandler:
- mOnStart/mOnRepeat/mOnEnd:执行动画监听回调的Runnable。
- mInterpolator:
- View.mCurrentAnimation:绑定到该View的动画。
- ViewGroup.mChildTransformation:供子View保存某帧动画的计算结果。所有子View共用1个,以节省空间。
创建动画
- 调用View.setAnimation()为View.mCurrentAnimation赋值。
执行动画
View.startAnimation()
- 把从该View回溯到DecorView标记为PFLAG_INVALIDATED;
- 在View绘制过程(onDraw())中:
- 如果View.mCurrentAnimation不为空,则调用View.applyLegacyAnimation()。在View.applyLegacyAnimation()中:
- 如果没有调用过Animation.initialized(),则调用Animation.initialized()重置所有参数,再调用Animation.initializeInvalidateRegion()记录下相对于该View左上角的原本的四边位置,再调用Animation.setListenerHandler(),把mAttatchInfo.mHandler赋值给Animation.mListenerHandler,mAttatchInfo.mHandler是向UI线程发信息的,所以动画监听回调都是在UI线程执行的。
- 由开始时间、动画时长、当前时间计算时间比率,再由差值器计算出动画因子,调用Animation.applyTransformation()。Animation子类重写applyTransformation(),以实现自己的动画逻辑。最终计算结果记录在ViewGroup.mChildTransformation。
- 调用软/硬件绘制方法用计算结果改变绘制结果。
View动画不影响触摸事件
一个按钮用View动画移动一段距离,要点击原位才有反应。因为View动画只改变绘制结果,不改变View的位置(mTop等),而触摸事件分发机制中,判断事件是否落到了该View上,使用的是View的位置。
Transition
重要的类
- Scene:封装了控件树的一个子树,表示需要执行动画的部分。
- mSceneRoot:执行动画的子树的根节点。
- mLayoutId:layout文件的ID,在入场时将用它替换mSceneRoot的子节点。
- mLayout:一个View树,在入场时将用它替换mSceneRoot的子节点。
- mEnterAction/mExitAction(Runnable):定义入场/出场时,除动画外的其它动作。
- Transition:比较两个Scene中相同控件的属性的不同,自动生成属性动画。
- mSceneRoot:场景。
- captureValues():计算动画参数。
- playTransition():创建并执行属性动画。
- TransitionManager:
- mSceneTransition:保存“一个场景进出”的动画。
- mScenePairTransition:保存“两个场景切换”的动画。
- sRunningTransition:保存所有正在执行的场景动画及其对应的子树。线程单例。
- mPendingTransition:保存所有正在执行的场景动画的子树。
- AnimationInfo:
- TransitionValues:
封装Scene
创建动画并执行
在TransitionManager.changeScene()中:
- 把Scene.mSceneRoot加入TransitionManager.mPendingTransition。
- 暂停所有Scene.mSceneRoot对应的动画。
- 执行Scene.mEnterAction。
- 为Scene.mSceneRoot添加ViewTreeObserver.OnPreDrawListener,在onPreDraw()回调中:
- 从TransitionManager.mPendingTransition中删除Scene.mSceneRoot。
- 把Transition对象加入TransitionManager.sRunningTransition。
- 为Transition对象添加监听:当动画执行完毕,从TransitionManager.sRunningTransition中删除Transition对象。
- 调用Transition.captureValues()。
- 恢复所有Scene.mSceneRoot对应的动画。
- 调用Transition.playTransition()。
定义Activity转场动画
定义主题的窗口动画
Activity.overridePendingTransition()
Content Transition
分为2步。先把A界面封装为StartScene1。然后所有控件的可见性设为INVISIBLE,并封装为EndScene1。用Transition1比较StartScene1和EndScene1的不同创建并执行属性动画。再把B界面所有控件的可见性设为INVISIBLE,封装为StartScene2。然后所有控件的可见性设为VISIBLE,并封装为EndScene2。用Transition2比较StartScene2和EndScene2的不同创建并执行属性动画。
getWindow().setExitTransition(); // 界面1跳转到界面2时,界面1的效果
getWindow().setEnterTransition(); // 界面1跳转到界面2时,界面2的效果
getWindow().setReturnTransition(); // 界面2跳回到界面1时,界面2的效果
getWindow().setReenterTransition(); // 界面2跳转到界面1时,界面1的效果
ActivityOptionsCompat.makeSceneTransitionAnimation(activity).toBundle(); // 把它作为startActivity()的第二个参数
Transition.excludeTarget(); // 指定某个控件不一起执行动画
Shared Element Transition
共享元素动画。可以和Content Transition一起使用。无法为每个共享元素单独设置动画效果。
getWindow().setSharedElementEnterTransition(); // 共享元素进入界面2时的动画
getWindow().setSharedElementReturnTransition(); // 共享元素返回界面1时的动画
ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pair...);
// 其中,pair是个数可变的Pair对象。Pair是可序列化的HashMap,以View为键,以String为值。
// 这里的String值,必须与界面2的对应的共享元素的transitionName一致。