接着上篇继续分析布局流程。
onLayoutChildren
所取onLayoutChildren是LinearLayoutManager的方法,RecyclerView把具体的布局交给了布局管理器去做,精简后的代码主要做了三件事:
一、确定锚点(updateAnchorInfoForLayout),所谓锚点就是布局的基准点,一般情况下layout子view是从上到下或者从下到上的,但是当子view有焦点时,比如子view是EditText输入时获得了焦点,那么会以该子view为锚点,分别向上和向下layout。还有一个问题是,如何得知应该从第几个子view开始layout,例如滑动之后怎么知道?事实上RecyclerView的滑动不会走onLayoutChildren方法(滑动不走measure和layout方法,直接调用fill修改布局),所以此处就只是简单沿用旧布局的position。
二、完成布局,调用fill函数完成布局。
三、处理动画(layoutForPredictiveAnimations),RecyclerView的dispatchLayoutStep1和dispatchLayoutStep3已经处理了一部分动画(见上篇),但是还不够。当子view被挤出屏幕时应该执行平移动画而不是淡出动画(淡出动画是被从Adapter的数据中彻底删除才用的),为了区分这里需要特殊处理。在布局完成后,被挤出屏幕的view实际上已经不在RecyclerView的mChildren数组中了,但是layoutForPredictiveAnimations会把该子view重新加进数组,不过是以DisappearingView的形式暂时存在,当平移动画结束立即会被删除。
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//确保生成LayoutState
ensureLayoutState();
mLayoutState.mRecycle = false;
// resolve layout direction
//是否需要反向布局
resolveShouldLayoutReverse();
//获取焦点,可能有可能没有
final View focused = getFocusedChild();
if (!mAnchorInfo.mValid || mPendingScrollPosition != RecyclerView.NO_POSITION
|| mPendingSavedState != null) {
mAnchorInfo.reset();
mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
// calculate anchor position and coordinate
//如果有mPendingSavedState,直接拿来用,如果子项view有焦点,以焦点为准,都没有则取头或者尾
updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
mAnchorInfo.mValid = true;
} else if (focused != null && (mOrientationHelper.getDecoratedStart(focused)
//焦点view被软键盘弹出屏幕时调用,把焦点view拉回屏幕顶端或底端
>= mOrientationHelper.getEndAfterPadding()
|| mOrientationHelper.getDecoratedEnd(focused)
<= mOrientationHelper.getStartAfterPadding())) {
mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused)