void dispatchLayout() {
if (mAdapter == null) {
Log.e(TAG, "No adapter attached; skipping layout");
return;
}
eatRequestLayout();
// simple animations are a subset of advanced animations (which will
// cause a
// prelayout step)
boolean animateChangesSimple = mItemAnimator != null
&& mItemsAddedOrRemoved && !mItemsChanged;
final boolean animateChangesAdvanced = ENABLE_PREDICTIVE_ANIMATIONS
&& animateChangesSimple && predictiveItemAnimationsEnabled();
mItemsAddedOrRemoved = mItemsChanged = false;
ArrayMap<View, Rect> appearingViewInitialBounds = null;
mState.mInPreLayout = animateChangesAdvanced;
mState.mItemCount = mAdapter.getItemCount();
if (animateChangesSimple) {
// Step 0: Find out where all non-removed items are, pre-layout
mState.mPreLayoutHolderMap.clear();
mState.mPostLayoutHolderMap.clear();
int count = getChildCount();
for (int i = 0; i < count; ++i) {
final ViewHolder holder = getChildViewHolderInt(getChildAt(i));
final View view = holder.itemView;
mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(
holder, view.getLeft(), view.getTop(), view.getRight(),
view.getBottom(), holder.mPosition));
}
}
if (animateChangesAdvanced) {
// Step 1: run prelayout: This will use the old positions of items.
// The layout manager
// is expected to layout everything, even removed items (though not
// to add removed
// items back to the container). This gives the pre-layout position
// of APPEARING views
// which come into existence as part of the real layout.
mInPreLayout = true;
final boolean didStructureChange = mState.mStructureChanged;
mState.mStructureChanged = false;
// temporarily disable flag because we are asking for previous
// layout
mLayout.onLayoutChildren(mRecycler, mState);/*****/
mState.mStructureChanged = didStructureChange;
mInPreLayout = false;
appearingViewInitialBounds = new ArrayMap<View, Rect>();
for (int i = 0; i < getChildCount(); ++i) {
boolean found = false;
View child = getChildAt(i);
for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {
ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);
if (holder.itemView == child) {
found = true;
continue;
}
}
if (!found) {
appearingViewInitialBounds.put(
child,
new Rect(child.getLeft(), child.getTop(), child
.getRight(), child.getBottom()));
}
}
}
clearOldPositions();
dispatchLayoutUpdates();
mState.mItemCount = mAdapter.getItemCount();
// Step 2: Run layout
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = false;
mPendingSavedState = null;
// onLayoutChildren may have caused client code to disable item
// animations; re-check
animateChangesSimple = animateChangesSimple && mItemAnimator != null;
if (animateChangesSimple) {
// Step 3: Find out where things are now, post-layout
int count = getChildCount();
for (int i = 0; i < count; ++i) {
ViewHolder holder = getChildViewHolderInt(getChildAt(i));
final View view = holder.itemView;
mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(
holder, view.getLeft(), view.getTop(), view.getRight(),
view.getBottom(), holder.mPosition));
}
// Step 4: Animate DISAPPEARING and REMOVED items
int preLayoutCount = mState.mPreLayoutHolderMap.size();
for (int i = preLayoutCount - 1; i >= 0; i--) {
ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);
if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {
ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap
.valueAt(i);
mState.mPreLayoutHolderMap.removeAt(i);
View disappearingItemView = disappearingItem.holder.itemView;
removeDetachedView(disappearingItemView, false);
mRecycler.unscrapView(disappearingItem.holder);
animateDisappearance(disappearingItem);
}
}
// Step 5: Animate APPEARING and ADDED items
int postLayoutCount = mState.mPostLayoutHolderMap.size();
if (postLayoutCount > 0) {
for (int i = postLayoutCount - 1; i >= 0; i--) {
ViewHolder itemHolder = mState.mPostLayoutHolderMap
.keyAt(i);
ItemHolderInfo info = mState.mPostLayoutHolderMap
.valueAt(i);
if ((mState.mPreLayoutHolderMap.isEmpty() || !mState.mPreLayoutHolderMap
.containsKey(itemHolder))) {
mState.mPostLayoutHolderMap.removeAt(i);
Rect initialBounds = (appearingViewInitialBounds != null) ? appearingViewInitialBounds
.get(itemHolder.itemView) : null;
animateAppearance(itemHolder, initialBounds, info.left,
info.top);
}
}
}
// Step 6: Animate PERSISTENT items
count = mState.mPostLayoutHolderMap.size();
for (int i = 0; i < count; ++i) {
ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);
ItemHolderInfo postInfo = mState.mPostLayoutHolderMap
.valueAt(i);
ItemHolderInfo preInfo = mState.mPreLayoutHolderMap
.get(postHolder);
if (preInfo != null && postInfo != null) {
if (preInfo.left != postInfo.left
|| preInfo.top != postInfo.top) {
postHolder.setIsRecyclable(false);
if (DEBUG) {
Log.d(TAG, "PERSISTENT: " + postHolder
+ " with view " + postHolder.itemView);
}
if (mItemAnimator.animateMove(postHolder, preInfo.left,
preInfo.top, postInfo.left, postInfo.top)) {
postAnimationRunner();
}
}
}
}
}
resumeRequestLayout(false);
mLayout.removeAndRecycleScrapInt(mRecycler, !animateChangesAdvanced);
mState.mPreviousLayoutItemCount = mState.mItemCount;
mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
}