blob: 15bbf8cea54aa001a9c2a4a7c4d3ae2b49885d00 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v7.widget;
import static android.support.v7.widget.RecyclerView.ItemAnimator.FLAG_CHANGED;
import static android.support.v7.widget.RecyclerView.ItemAnimator.FLAG_MOVED;
import static android.support.v7.widget.RecyclerView.ItemAnimator.FLAG_REMOVED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
import android.view.View;
import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Includes tests for the new RecyclerView animations API (v2).
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class ItemAnimatorV2ApiTest extends BaseRecyclerViewAnimationsTest {
@Override
protected RecyclerView.ItemAnimator createItemAnimator() {
return mAnimator;
}
@Test
public void changeMovedOutside() throws Throwable {
setupBasic(10);
final RecyclerView.ViewHolder target = mRecyclerView.findViewHolderForAdapterPosition(9);
mLayoutManager.expectLayouts(2);
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 9;
mTestAdapter.changeAndNotify(9, 1);
mLayoutManager.waitForLayout(2);
// changed item should not be laid out and should just receive disappear
LoggingInfo pre = mAnimator.preLayoutInfoMap.get(target);
assertNotNull("test sanity", pre);
assertNull("test sanity", mAnimator.postLayoutInfoMap.get(target));
assertTrue(mAnimator.animateChangeList.isEmpty());
assertEquals(1, mAnimator.animateDisappearanceList.size());
assertEquals(new AnimateDisappearance(target, pre, null),
mAnimator.animateDisappearanceList.get(0));
// This is kind of problematic because layout manager will never layout the updated
// version of this view since it went out of bounds and it won't show up in scrap.
// I don't think we can do much better since other option is to bind a fresh view
}
@Test
public void changeMovedOutsideWithPredictiveAndTwoViewHolders() throws Throwable {
final RecyclerView.ViewHolder[] targets = new RecyclerView.ViewHolder[2];
setupBasic(10, 0, 10, new TestAdapter(10) {
@Override
public void onBindViewHolder(TestViewHolder holder,
int position) {
super.onBindViewHolder(holder, position);
if (position == 0) {
if (targets[0] == null) {
targets[0] = holder;
} else {
assertThat(targets[1], CoreMatchers.nullValue());
targets[1] = holder;
}
}
}
});
final RecyclerView.ViewHolder singleItemTarget =
mRecyclerView.findViewHolderForAdapterPosition(1);
mAnimator.canReUseCallback = new CanReUseCallback() {
@Override
public boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
return viewHolder == singleItemTarget;
}
};
mLayoutManager.expectLayouts(2);
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void onLayoutChildren(RecyclerView.Recycler recycler,
AnimationLayoutManager lm, RecyclerView.State state) {
super.onLayoutChildren(recycler, lm, state);
if (!state.isPreLayout()) {
mLayoutManager.addDisappearingView(recycler.getViewForPosition(0));
mLayoutManager.addDisappearingView(recycler.getScrapList().get(0).itemView);
}
}
};
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 8;
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 2;
mTestAdapter.changeAndNotify(0, 2);
mLayoutManager.waitForLayout(2);
checkForMainThreadException();
final RecyclerView.ViewHolder oldTarget = targets[0];
final RecyclerView.ViewHolder newTarget = targets[1];
assertNotNull("test sanity", targets[0]);
assertNotNull("test sanity", targets[1]);
// changed item should not be laid out and should just receive disappear
LoggingInfo pre = mAnimator.preLayoutInfoMap.get(oldTarget);
assertNotNull("test sanity", pre);
assertNull("test sanity", mAnimator.postLayoutInfoMap.get(oldTarget));
assertNull("test sanity", mAnimator.preLayoutInfoMap.get(newTarget));
LoggingInfo post = mAnimator.postLayoutInfoMap.get(newTarget);
assertNotNull("test sanity", post);
assertEquals(1, mAnimator.animateChangeList.size());
assertEquals(1, mAnimator.animateDisappearanceList.size());
assertEquals(new AnimateChange(oldTarget, newTarget, pre, post),
mAnimator.animateChangeList.get(0));
LoggingInfo singleItemPre = mAnimator.preLayoutInfoMap.get(singleItemTarget);
assertNotNull("test sanity", singleItemPre);
LoggingInfo singleItemPost = mAnimator.postLayoutInfoMap.get(singleItemTarget);
assertNotNull("test sanity", singleItemPost);
assertEquals(new AnimateDisappearance(singleItemTarget, singleItemPre, singleItemPost),
mAnimator.animateDisappearanceList.get(0));
}
@Test
public void changeMovedOutsideWithPredictive() throws Throwable {
setupBasic(10);
final RecyclerView.ViewHolder target = mRecyclerView.findViewHolderForAdapterPosition(0);
mLayoutManager.expectLayouts(2);
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void onLayoutChildren(RecyclerView.Recycler recycler,
AnimationLayoutManager lm, RecyclerView.State state) {
super.onLayoutChildren(recycler, lm, state);
List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
assertThat(scrapList.size(), CoreMatchers.is(2));
mLayoutManager.addDisappearingView(scrapList.get(0).itemView);
mLayoutManager.addDisappearingView(scrapList.get(0).itemView);
}
};
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 8;
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 2;
mTestAdapter.changeAndNotify(0, 2);
mLayoutManager.waitForLayout(2);
checkForMainThreadException();
// changed item should not be laid out and should just receive disappear
LoggingInfo pre = mAnimator.preLayoutInfoMap.get(target);
assertNotNull("test sanity", pre);
LoggingInfo postInfo = mAnimator.postLayoutInfoMap.get(target);
assertNotNull("test sanity", postInfo);
assertTrue(mAnimator.animateChangeList.isEmpty());
assertEquals(2, mAnimator.animateDisappearanceList.size());
try {
assertEquals(new AnimateDisappearance(target, pre, postInfo),
mAnimator.animateDisappearanceList.get(0));
} catch (Throwable t) {
assertEquals(new AnimateDisappearance(target, pre, postInfo),
mAnimator.animateDisappearanceList.get(1));
}
}
@Test
public void simpleAdd() throws Throwable {
setupBasic(10);
mLayoutManager.expectLayouts(2);
mTestAdapter.addAndNotify(2, 1);
mLayoutManager.waitForLayout(2);
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(2);
assertEquals(1, mAnimator.animateAppearanceList.size());
AnimateAppearance log = mAnimator.animateAppearanceList.get(0);
assertSame(vh, log.viewHolder);
assertNull(log.preInfo);
assertEquals(0, log.postInfo.changeFlags);
// the first two should not receive anything
for (int i = 0; i < 2; i++) {
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(0, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
for (int i = 3; i < mTestAdapter.getItemCount(); i++) {
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(FLAG_MOVED, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
checkForMainThreadException();
}
@Test
public void simpleRemove() throws Throwable {
setupBasic(10);
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(2);
mLayoutManager.expectLayouts(2);
mTestAdapter.deleteAndNotify(2, 1);
mLayoutManager.waitForLayout(2);
checkForMainThreadException();
assertEquals(1, mAnimator.animateDisappearanceList.size());
AnimateDisappearance log = mAnimator.animateDisappearanceList.get(0);
assertSame(vh, log.viewHolder);
assertFalse(mAnimator.postLayoutInfoMap.containsKey(vh));
assertEquals(FLAG_REMOVED, log.preInfo.changeFlags);
// the first two should not receive anything
for (int i = 0; i < 2; i++) {
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(0, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
for (int i = 3; i < mTestAdapter.getItemCount(); i++) {
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(FLAG_MOVED, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
checkForMainThreadException();
}
@Test
public void simpleUpdate() throws Throwable {
setupBasic(10);
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(2);
mLayoutManager.expectLayouts(2);
mTestAdapter.changeAndNotify(2, 1);
mLayoutManager.waitForLayout(2);
assertEquals(1, mAnimator.animateChangeList.size());
AnimateChange log = mAnimator.animateChangeList.get(0);
assertSame(vh, log.viewHolder);
assertSame(vh, log.newHolder);
assertTrue(mAnimator.preLayoutInfoMap.containsKey(vh));
assertTrue(mAnimator.postLayoutInfoMap.containsKey(vh));
assertEquals(FLAG_CHANGED, log.preInfo.changeFlags);
assertEquals(0, log.postInfo.changeFlags);
//others should not receive anything
for (int i = 0; i < mTestAdapter.getItemCount(); i++) {
if (i == 2) {
continue;
}
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(0, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
checkForMainThreadException();
}
@Test
public void updateWithDuplicateViewHolder() throws Throwable {
setupBasic(10);
final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(2);
mAnimator.canReUseCallback = new CanReUseCallback() {
@Override
public boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
assertSame(viewHolder, vh);
return false;
}
};
mLayoutManager.expectLayouts(2);
mTestAdapter.changeAndNotify(2, 1);
mLayoutManager.waitForLayout(2);
final RecyclerView.ViewHolder newVh = mRecyclerView.findViewHolderForAdapterPosition(2);
assertNotSame(vh, newVh);
assertEquals(1, mAnimator.animateChangeList.size());
AnimateChange log = mAnimator.animateChangeList.get(0);
assertSame(vh, log.viewHolder);
assertSame(newVh, log.newHolder);
assertNull(vh.itemView.getParent());
assertTrue(mAnimator.preLayoutInfoMap.containsKey(vh));
assertFalse(mAnimator.postLayoutInfoMap.containsKey(vh));
assertTrue(mAnimator.postLayoutInfoMap.containsKey(newVh));
assertEquals(FLAG_CHANGED, log.preInfo.changeFlags);
assertEquals(0, log.postInfo.changeFlags);
//others should not receive anything
for (int i = 0; i < mTestAdapter.getItemCount(); i++) {
if (i == 2) {
continue;
}
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(0, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
checkForMainThreadException();
}
@Test
public void updateWithOneDuplicateAndOneInPlace() throws Throwable {
setupBasic(10);
final RecyclerView.ViewHolder replaced = mRecyclerView.findViewHolderForAdapterPosition(2);
final RecyclerView.ViewHolder reused = mRecyclerView.findViewHolderForAdapterPosition(3);
mAnimator.canReUseCallback = new CanReUseCallback() {
@Override
public boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
if (viewHolder == replaced) {
return false;
} else if (viewHolder == reused) {
return true;
}
fail("unpexpected view");
return false;
}
};
mLayoutManager.expectLayouts(2);
mTestAdapter.changeAndNotify(2, 2);
mLayoutManager.waitForLayout(2);
final RecyclerView.ViewHolder newVh = mRecyclerView.findViewHolderForAdapterPosition(2);
assertNotSame(replaced, newVh);
assertSame(reused, mRecyclerView.findViewHolderForAdapterPosition(3));
assertEquals(2, mAnimator.animateChangeList.size());
AnimateChange logReplaced = null, logReused = null;
for (AnimateChange change : mAnimator.animateChangeList) {
if (change.newHolder == change.viewHolder) {
logReused = change;
} else {
logReplaced = change;
}
}
assertNotNull(logReplaced);
assertNotNull(logReused);
assertSame(replaced, logReplaced.viewHolder);
assertSame(newVh, logReplaced.newHolder);
assertSame(reused, logReused.viewHolder);
assertSame(reused, logReused.newHolder);
assertTrue(mAnimator.preLayoutInfoMap.containsKey(replaced));
assertTrue(mAnimator.preLayoutInfoMap.containsKey(reused));
assertTrue(mAnimator.postLayoutInfoMap.containsKey(newVh));
assertTrue(mAnimator.postLayoutInfoMap.containsKey(reused));
assertFalse(mAnimator.postLayoutInfoMap.containsKey(replaced));
assertEquals(FLAG_CHANGED, logReplaced.preInfo.changeFlags);
assertEquals(FLAG_CHANGED, logReused.preInfo.changeFlags);
assertEquals(0, logReplaced.postInfo.changeFlags);
assertEquals(0, logReused.postInfo.changeFlags);
//others should not receive anything
for (int i = 0; i < mTestAdapter.getItemCount(); i++) {
if (i == 2 || i == 3) {
continue;
}
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(0, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
checkForMainThreadException();
}
@Test
public void changeToDisappear() throws Throwable {
setupBasic(10);
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(9);
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 9;
mLayoutManager.expectLayouts(2);
mTestAdapter.changeAndNotify(9, 1);
mLayoutManager.waitForLayout(2);
assertEquals(1, mAnimator.animateDisappearanceList.size());
AnimateDisappearance log = mAnimator.animateDisappearanceList.get(0);
assertSame(vh, log.viewHolder);
assertFalse(mAnimator.postLayoutInfoMap.containsKey(vh));
assertEquals(FLAG_CHANGED, log.preInfo.changeFlags);
assertEquals(0, mAnimator.animateChangeList.size());
assertEquals(0, mAnimator.animateAppearanceList.size());
assertEquals(9, mAnimator.animatePersistenceList.size());
checkForMainThreadException();
}
@Test
public void changeToDisappearFromHead() throws Throwable {
setupBasic(10);
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(0);
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 9;
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
mLayoutManager.expectLayouts(2);
mTestAdapter.changeAndNotify(0, 1);
mLayoutManager.waitForLayout(2);
assertEquals(1, mAnimator.animateDisappearanceList.size());
AnimateDisappearance log = mAnimator.animateDisappearanceList.get(0);
assertSame(vh, log.viewHolder);
assertFalse(mAnimator.postLayoutInfoMap.containsKey(vh));
assertEquals(FLAG_CHANGED, log.preInfo.changeFlags);
assertEquals(0, mAnimator.animateChangeList.size());
assertEquals(0, mAnimator.animateAppearanceList.size());
assertEquals(9, mAnimator.animatePersistenceList.size());
checkForMainThreadException();
}
@Test
public void updatePayload() throws Throwable {
setupBasic(10);
final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(2);
final Object payload = new Object();
mAnimator.canReUseCallback = new CanReUseCallback() {
@Override
public boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
assertSame(vh, viewHolder);
assertEquals(1, payloads.size());
assertSame(payload, payloads.get(0));
return true;
}
};
mLayoutManager.expectLayouts(2);
mTestAdapter.changeAndNotifyWithPayload(2, 1, payload);
mLayoutManager.waitForLayout(2);
assertEquals(1, mAnimator.animateChangeList.size());
AnimateChange log = mAnimator.animateChangeList.get(0);
assertSame(vh, log.viewHolder);
assertSame(vh, log.newHolder);
assertTrue(mAnimator.preLayoutInfoMap.containsKey(vh));
assertTrue(mAnimator.postLayoutInfoMap.containsKey(vh));
assertEquals(FLAG_CHANGED, log.preInfo.changeFlags);
assertEquals(0, log.postInfo.changeFlags);
assertNotNull(log.preInfo.payloads);
assertTrue(log.preInfo.payloads.contains(payload));
//others should not receive anything
for (int i = 0; i < mTestAdapter.getItemCount(); i++) {
if (i == 2) {
continue;
}
RecyclerView.ViewHolder other = mRecyclerView.findViewHolderForAdapterPosition(i);
assertEquals(0, mAnimator.preLayoutInfoMap.get(other).changeFlags);
}
checkForMainThreadException();
}
@Test
public void notifyDataSetChanged() throws Throwable {
TestAdapter adapter = new TestAdapter(10);
adapter.setHasStableIds(true);
setupBasic(10, 0, 10, adapter);
mLayoutManager.expectLayouts(1);
mTestAdapter.dispatchDataSetChanged();
mLayoutManager.waitForLayout(2);
assertEquals(10, mAnimator.animateChangeList.size());
for (AnimateChange change : mAnimator.animateChangeList) {
assertNotNull(change.preInfo);
assertNotNull(change.postInfo);
assertSame(change.preInfo.viewHolder, change.postInfo.viewHolder);
}
assertEquals(0, mAnimator.animatePersistenceList.size());
assertEquals(0, mAnimator.animateAppearanceList.size());
assertEquals(0, mAnimator.animateDisappearanceList.size());
}
@Test
public void notifyDataSetChangedWithoutStableIds() throws Throwable {
TestAdapter adapter = new TestAdapter(10);
adapter.setHasStableIds(false);
setupBasic(10, 0, 10, adapter);
mLayoutManager.expectLayouts(1);
mTestAdapter.dispatchDataSetChanged();
mLayoutManager.waitForLayout(2);
assertEquals(0, mAnimator.animateChangeList.size());
assertEquals(0, mAnimator.animatePersistenceList.size());
assertEquals(0, mAnimator.animateAppearanceList.size());
assertEquals(0, mAnimator.animateDisappearanceList.size());
}
@Test
public void notifyDataSetChangedWithAppearing() throws Throwable {
notifyDataSetChangedWithAppearing(false);
}
@Test
public void notifyDataSetChangedWithAppearingNotifyBoth() throws Throwable {
notifyDataSetChangedWithAppearing(true);
}
public void notifyDataSetChangedWithAppearing(final boolean notifyBoth) throws Throwable {
final TestAdapter adapter = new TestAdapter(10);
adapter.setHasStableIds(true);
setupBasic(10, 0, 10, adapter);
mLayoutManager.expectLayouts(1);
runTestOnUiThread(new Runnable() {
@Override
public void run() {
try {
if (notifyBoth) {
adapter.addAndNotify(2, 2);
} else {
adapter.mItems.add(2, new Item(2, "custom 1"));
adapter.mItems.add(3, new Item(3, "custom 2"));
}
adapter.notifyDataSetChanged();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
});
mLayoutManager.waitForLayout(2);
assertEquals(10, mAnimator.animateChangeList.size());
assertEquals(0, mAnimator.animatePersistenceList.size());
assertEquals(2, mAnimator.animateAppearanceList.size());
assertEquals(0, mAnimator.animateDisappearanceList.size());
}
@Test
public void notifyDataSetChangedWithDispappearing() throws Throwable {
notifyDataSetChangedWithDispappearing(false);
}
@Test
public void notifyDataSetChangedWithDispappearingNotifyBoth() throws Throwable {
notifyDataSetChangedWithDispappearing(true);
}
public void notifyDataSetChangedWithDispappearing(final boolean notifyBoth) throws Throwable {
final TestAdapter adapter = new TestAdapter(10);
adapter.setHasStableIds(true);
setupBasic(10, 0, 10, adapter);
mLayoutManager.expectLayouts(1);
runTestOnUiThread(new Runnable() {
@Override
public void run() {
try {
if (notifyBoth) {
adapter.deleteAndNotify(2, 2);
} else {
adapter.mItems.remove(2);
adapter.mItems.remove(2);
}
adapter.notifyDataSetChanged();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
});
mLayoutManager.waitForLayout(2);
assertEquals(8, mAnimator.animateChangeList.size());
assertEquals(0, mAnimator.animatePersistenceList.size());
assertEquals(0, mAnimator.animateAppearanceList.size());
assertEquals(2, mAnimator.animateDisappearanceList.size());
}
@Test
public void notifyUpdateWithChangedAdapterType() throws Throwable {
final AtomicInteger itemType = new AtomicInteger(1);
final TestAdapter adapter = new TestAdapter(10) {
@Override
public int getItemViewType(int position) {
return position == 2 ? itemType.get() : 20;
}
};
adapter.setHasStableIds(true);
setupBasic(10, 0, 10, adapter);
final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(2);
mAnimator.canReUseCallback = new CanReUseCallback() {
@Override
public boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
return viewHolder != vh;
}
};
mLayoutManager.expectLayouts(1);
itemType.set(3);
adapter.dispatchDataSetChanged();
mLayoutManager.waitForLayout(2);
final RecyclerView.ViewHolder newVh = mRecyclerView.findViewHolderForAdapterPosition(2);
// TODO we should be able to map old type to the new one but doing that change has some
// recycling side effects.
assertEquals(9, mAnimator.animateChangeList.size());
assertEquals(0, mAnimator.animatePersistenceList.size());
assertEquals(1, mAnimator.animateAppearanceList.size());
assertEquals(0, mAnimator.animateDisappearanceList.size());
assertNotSame(vh, newVh);
for (AnimateChange change : mAnimator.animateChangeList) {
if (change.viewHolder == vh) {
assertSame(change.newHolder, newVh);
assertSame(change.viewHolder, vh);
} else {
assertSame(change.newHolder, change.viewHolder);
}
}
}
LoggingV2Animator mAnimator = new LoggingV2Animator();
class LoggingV2Animator extends RecyclerView.ItemAnimator {
CanReUseCallback canReUseCallback = new CanReUseCallback() {
@Override
public boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
return true;
}
};
Map<RecyclerView.ViewHolder, LoggingInfo> preLayoutInfoMap = new HashMap<>();
Map<RecyclerView.ViewHolder, LoggingInfo> postLayoutInfoMap = new HashMap<>();
List<AnimateAppearance> animateAppearanceList = new ArrayList<>();
List<AnimateDisappearance> animateDisappearanceList = new ArrayList<>();
List<AnimatePersistence> animatePersistenceList = new ArrayList<>();
List<AnimateChange> animateChangeList = new ArrayList<>();
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder,
List<Object> payloads) {
return canReUseCallback.canReUse(viewHolder, payloads);
}
@NonNull
@Override
public ItemHolderInfo recordPreLayoutInformation(@NonNull RecyclerView.State state,
@NonNull RecyclerView.ViewHolder viewHolder,
@AdapterChanges int changeFlags, @NonNull List<Object> payloads) {
LoggingInfo loggingInfo = new LoggingInfo(viewHolder, changeFlags, payloads);
preLayoutInfoMap.put(viewHolder, loggingInfo);
return loggingInfo;
}
@NonNull
@Override
public ItemHolderInfo recordPostLayoutInformation(@NonNull RecyclerView.State state,
@NonNull RecyclerView.ViewHolder viewHolder) {
LoggingInfo loggingInfo = new LoggingInfo(viewHolder, 0, null);
postLayoutInfoMap.put(viewHolder, loggingInfo);
return loggingInfo;
}
@Override
public boolean animateDisappearance(@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull ItemHolderInfo preLayoutInfo,
@Nullable ItemHolderInfo postLayoutInfo) {
animateDisappearanceList.add(new AnimateDisappearance(viewHolder,
(LoggingInfo) preLayoutInfo, (LoggingInfo) postLayoutInfo));
assertSame(preLayoutInfoMap.get(viewHolder), preLayoutInfo);
assertSame(postLayoutInfoMap.get(viewHolder), postLayoutInfo);
dispatchAnimationFinished(viewHolder);
return false;
}
@Override
public boolean animateAppearance(@NonNull RecyclerView.ViewHolder viewHolder,
ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
animateAppearanceList.add(
new AnimateAppearance(viewHolder, (LoggingInfo) preInfo, (LoggingInfo) postInfo));
assertSame(preLayoutInfoMap.get(viewHolder), preInfo);
assertSame(postLayoutInfoMap.get(viewHolder), postInfo);
dispatchAnimationFinished(viewHolder);
return false;
}
@Override
public boolean animatePersistence(@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
animatePersistenceList.add(new AnimatePersistence(viewHolder, (LoggingInfo) preInfo,
(LoggingInfo) postInfo));
dispatchAnimationFinished(viewHolder);
assertSame(preLayoutInfoMap.get(viewHolder), preInfo);
assertSame(postLayoutInfoMap.get(viewHolder), postInfo);
return false;
}
@Override
public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder,
@NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preInfo,
@NonNull ItemHolderInfo postInfo) {
animateChangeList.add(new AnimateChange(oldHolder, newHolder, (LoggingInfo) preInfo,
(LoggingInfo) postInfo));
if (oldHolder != null) {
dispatchAnimationFinished(oldHolder);
assertSame(preLayoutInfoMap.get(oldHolder), preInfo);
}
if (newHolder != null && oldHolder != newHolder) {
dispatchAnimationFinished(newHolder);
assertSame(postLayoutInfoMap.get(newHolder), postInfo);
}
return false;
}
@Override
public void runPendingAnimations() {
}
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
}
@Override
public void endAnimations() {
}
@Override
public boolean isRunning() {
return false;
}
}
interface CanReUseCallback {
boolean canReUse(RecyclerView.ViewHolder viewHolder, List<Object> payloads);
}
}