Android布局动画梳理之LayoutTransition源码追踪
Android系统的布局动画可以按如下脉络进行梳理
1、按时间点分类 版本4.0之前可能没有,4.0或以上有LayoutTransition,4.4.2或以上有scenes+transition
2、按动作划分 child自身的动画 child移动的动画 可能的组合 被增删的child的自身动画 被影响的其他child的自身动画及移动动画
3、按涉及的部件划分 ViewGroup或其子类 LayoutTransition
4、需要梳理的问题 被增删的child的自身动画-------就是单个view的动画,这个无需梳理 受影响的其他child的自身动画 何时、怎样调用item的动画? 动画模板是怎样构建的?child怎样拷贝这个模板的? 受影响的其他child的移动动画(重点) 是不是有一个预布局? 留意一个监听布局改变的listener child移动动画的初始值和结束值是怎样计算的?又怎样调用的? 可能只有ViewGroup的增删动作才触发动画? 如果adapter的数据变化,又怎样才能触发动画? 4.4.2之后的框架是怎么回事? 其他child的自身动画和移动动画是怎样叠加的?
5、相关知识点 都有哪些布局计算或操作?比如requestLayout()之类 View体系中各个部件之间是怎样通过设置Listener来互相通信及同步的?
6、梳理策略 从ApiDemo里面的LayoutTransition开始 其后梳理由scenes+transition构成的Transitions framework
以下是从ViewGroup.addView()追踪到LayoutTransition源码的过程及结论
ViewGroup类
addView(View child) / addView(View child, int index) / addView(View child, int width, int height) addView(View child, int index, LayoutParams params) addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) mTransition.addChild(this, child) //从这里进入LayoutTransition类源码 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); onViewAdded(child);//这里可能只是一个回调通知而已 mOnHierarchyChangeListener.onChildViewAdded(this, child); childHasTransientStateChanged(child, true);
removeView(View view) //和addView基本类似 removeViewInternal(int index, View view) mTransition.removeChild(this, view); addDisappearingView(view); view.dispatchDetachedFromWindow(); removeFromArray(index); onViewRemoved(view); requestLayout();
结论 在ViewGroup中应该只是一个增删child及重新布局的刷新操作 没有看到受影响的其他child的相关操作,这些可能都在LayoutTransition里面
LayoutTransition类
addChild(ViewGroup parent, View child) addChild(ViewGroup parent, View child, boolean changesLayout) runChangeTransition(parent, child, APPEARING); loop: setupChangeAnimation(parent, changeReason, baseAnimator, duration, child);设置其他child的动画 anim.setTarget(child);anim.setupStartValues();//设置当前child状态为动画初始值 PropertyValuesHolder.setupValue(Object target, Keyframe kf) //通过反射机制获取target的属性值 View.OnLayoutChangeListener(){anim.setupEndValues();} //在重新布局时获取动画结束值 parent.requestTransitionStart(LayoutTransition.this);//估计在这里启动动画 loop: setupChangeAnimation((ViewGroup)parentParent, changeReason, parentAnimator, duration, tempParent);设置各级祖先层的动画 runAppearingTransition(parent, child);//这里面实际上没有什么Transition,仅仅是启动用户设置的mAppearingAnim
removeChild(ViewGroup parent, View child) removeChild(ViewGroup parent, View child, boolean changesLayout) runChangeTransition(parent, child, DISAPPEARING);//和addChild共用一个函数,只是选择了另一个动画 runDisappearingTransition(parent, child);//里面没有Transition,直接启动mDisappearingAnim
结论 增删child导致其他child的布局发生变化,下面是“其他child”的移动动画构建流程 在布局修改前保存child状态为Transition动画的初始值 设置一个布局改变监听器 在布局改变后(可能在绘制前)保存child状态为Transition动画的结束值 启动动画 其他child的自身动画和移动动画是怎样叠加的?直接先后调用runChangeTransition和runAppearingTransition 期间有布局请求 通过布局监听器来实现一系列顺序操作