RecyclerViewAnimationsTestpublic class RecyclerViewAnimationsTest extends BaseRecyclerViewInstrumentationTest
Fields Summary |
---|
private static final boolean | DEBUG | private static final String | TAG | AnimationLayoutManager | mLayoutManager | TestAdapter | mTestAdapter |
Constructors Summary |
---|
public RecyclerViewAnimationsTest()
super(DEBUG);
|
Methods Summary |
---|
public void | changeAnimTest(boolean supportsChangeAnim, boolean changeType, boolean hasStableIds, boolean deleteSomeItems)
final int changedIndex = 3;
final int defaultType = 1;
final AtomicInteger changedIndexNewType = new AtomicInteger(defaultType);
final String logPrefix = "supportsChangeAnim:" + supportsChangeAnim +
", change view type:" + changeType +
", has stable ids:" + hasStableIds +
", force predictive:" + deleteSomeItems;
TestAdapter testAdapter = new TestAdapter(10) {
@Override
public int getItemViewType(int position) {
return position == changedIndex ? changedIndexNewType.get() : defaultType;
}
@Override
public TestViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
if (DEBUG) {
Log.d(TAG, logPrefix + " onCreateVH" + vh.toString());
}
return vh;
}
@Override
public void onBindViewHolder(TestViewHolder holder,
int position) {
super.onBindViewHolder(holder, position);
if (DEBUG) {
Log.d(TAG, logPrefix + " onBind to " + position + "" + holder.toString());
}
}
};
testAdapter.setHasStableIds(hasStableIds);
setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter);
mRecyclerView.getItemAnimator().setSupportsChangeAnimations(supportsChangeAnim);
final RecyclerView.ViewHolder toBeChangedVH =
mRecyclerView.findViewHolderForLayoutPosition(changedIndex);
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void afterPreLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager layoutManager,
RecyclerView.State state) {
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(
changedIndex);
if (supportsChangeAnim) {
assertTrue(logPrefix + " changed view holder should have correct flag"
, vh.isChanged());
} else {
assertFalse(logPrefix + " changed view holder should have correct flag"
, vh.isChanged());
}
}
@Override
void afterPostLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager layoutManager, RecyclerView.State state) {
RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(
changedIndex);
assertFalse(logPrefix + "VH should not be marked as changed", vh.isChanged());
if (supportsChangeAnim) {
assertNotSame(logPrefix + "a new VH should be given if change is supported",
toBeChangedVH, vh);
} else if (!changeType && hasStableIds) {
assertSame(logPrefix + "if change animations are not supported but we have "
+ "stable ids, same view holder should be returned", toBeChangedVH, vh);
}
super.beforePostLayout(recycler, layoutManager, state);
}
};
mLayoutManager.expectLayouts(1);
if (changeType) {
changedIndexNewType.set(defaultType + 1);
}
if (deleteSomeItems) {
runTestOnUiThread(new Runnable() {
@Override
public void run() {
try {
mTestAdapter.deleteAndNotify(changedIndex + 2, 1);
mTestAdapter.notifyItemChanged(3);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
});
} else {
mTestAdapter.notifyItemChanged(3);
}
mLayoutManager.waitForLayout(2);
| public java.util.Map | collectPositions(RecyclerView recyclerView, RecyclerView.Recycler recycler, RecyclerView.State state, int positions)
Map<Integer, CollectPositionResult> positionToAdapterMapping
= new HashMap<Integer, CollectPositionResult>();
for (int position : positions) {
if (position < 0) {
continue;
}
positionToAdapterMapping.put(position,
findByPos(recyclerView, recycler, state, position));
}
return positionToAdapterMapping;
| private android.support.v7.widget.RecyclerViewAnimationsTest$CollectPositionResult | findByPos(RecyclerView recyclerView, RecyclerView.Recycler recycler, RecyclerView.State state, int position)
View view = recycler.getViewForPosition(position, true);
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
if (vh.wasReturnedFromScrap()) {
vh.clearReturnedFromScrapFlag(); //keep data consistent.
return CollectPositionResult.fromScrap(vh);
} else {
return CollectPositionResult.fromAdapter(vh);
}
| public void | getItemForDeletedViewTest(boolean stableIds)
final Set<Integer> itemViewTypeQueries = new HashSet<Integer>();
final Set<Integer> itemIdQueries = new HashSet<Integer>();
TestAdapter adapter = new TestAdapter(10) {
@Override
public int getItemViewType(int position) {
itemViewTypeQueries.add(position);
return super.getItemViewType(position);
}
@Override
public long getItemId(int position) {
itemIdQueries.add(position);
return mItems.get(position).mId;
}
};
adapter.setHasStableIds(stableIds);
setupBasic(10, 0, 10, adapter);
assertEquals("getItemViewType for all items should be called", 10,
itemViewTypeQueries.size());
if (adapter.hasStableIds()) {
assertEquals("getItemId should be called when adapter has stable ids", 10,
itemIdQueries.size());
} else {
assertEquals("getItemId should not be called when adapter does not have stable ids", 0,
itemIdQueries.size());
}
itemViewTypeQueries.clear();
itemIdQueries.clear();
mLayoutManager.expectLayouts(2);
// delete last two
final int deleteStart = 8;
final int deleteCount = adapter.getItemCount() - deleteStart;
adapter.deleteAndNotify(deleteStart, deleteCount);
mLayoutManager.waitForLayout(2);
for (int i = 0; i < deleteStart; i++) {
assertTrue("getItemViewType for existing item " + i + " should be called",
itemViewTypeQueries.contains(i));
if (adapter.hasStableIds()) {
assertTrue("getItemId for existing item " + i
+ " should be called when adapter has stable ids",
itemIdQueries.contains(i));
}
}
for (int i = deleteStart; i < deleteStart + deleteCount; i++) {
assertFalse("getItemViewType for deleted item " + i + " SHOULD NOT be called",
itemViewTypeQueries.contains(i));
if (adapter.hasStableIds()) {
assertFalse("getItemId for deleted item " + i + " SHOULD NOT be called",
itemIdQueries.contains(i));
}
}
| public android.support.v7.widget.RecyclerViewAnimationsTest$TestRecyclerView | getTestRecyclerView()
return (TestRecyclerView) mRecyclerView;
| public void | positionStatesTest(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount, android.support.v7.widget.RecyclerViewAnimationsTest$AdapterOps adapterChanges, android.support.v7.widget.RecyclerViewAnimationsTest$PositionConstraint constraints)
positionStatesTest(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null,
adapterChanges, constraints);
| public void | positionStatesTest(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount, TestAdapter adapter, android.support.v7.widget.RecyclerViewAnimationsTest$AdapterOps adapterChanges, android.support.v7.widget.RecyclerViewAnimationsTest$PositionConstraint constraints)
setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, adapter);
mLayoutManager.expectLayouts(2);
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void beforePreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
RecyclerView.State state) {
super.beforePreLayout(recycler, lm, state);
//harmless
lm.detachAndScrapAttachedViews(recycler);
final int[] ids = new int[constraints.length];
for (int i = 0; i < constraints.length; i++) {
ids[i] = constraints[i].mPreLayoutPos;
}
Map<Integer, CollectPositionResult> positions
= collectPositions(lm.mRecyclerView, recycler, state, ids);
for (PositionConstraint constraint : constraints) {
if (constraint.mPreLayoutPos != -1) {
constraint.validate(state, positions.get(constraint.mPreLayoutPos),
lm.getLog());
}
}
}
@Override
void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
RecyclerView.State state) {
super.beforePostLayout(recycler, lm, state);
lm.detachAndScrapAttachedViews(recycler);
final int[] ids = new int[constraints.length];
for (int i = 0; i < constraints.length; i++) {
ids[i] = constraints[i].mPostLayoutPos;
}
Map<Integer, CollectPositionResult> positions
= collectPositions(lm.mRecyclerView, recycler, state, ids);
for (PositionConstraint constraint : constraints) {
if (constraint.mPostLayoutPos >= 0) {
constraint.validate(state, positions.get(constraint.mPostLayoutPos),
lm.getLog());
}
}
}
};
adapterChanges.run(mTestAdapter);
mLayoutManager.waitForLayout(2);
checkForMainThreadException();
for (PositionConstraint constraint : constraints) {
constraint.assertValidate();
}
| private void | setExpectedItemCounts(int preLayout, int postLayout)
mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(preLayout, postLayout);
| private void | setLayoutRange(int start, int count)
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = start;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = count;
| protected void | setUp()
super.setUp();
| RecyclerView | setupBasic(int itemCount)
return setupBasic(itemCount, 0, itemCount);
| RecyclerView | setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount)
return setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null);
| RecyclerView | setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount, TestAdapter testAdapter)
final TestRecyclerView recyclerView = new TestRecyclerView(getActivity());
recyclerView.setHasFixedSize(true);
if (testAdapter == null) {
mTestAdapter = new TestAdapter(itemCount);
} else {
mTestAdapter = testAdapter;
}
recyclerView.setAdapter(mTestAdapter);
mLayoutManager = new AnimationLayoutManager();
recyclerView.setLayoutManager(mLayoutManager);
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = firstLayoutStartIndex;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = firstLayoutItemCount;
mLayoutManager.expectLayouts(1);
recyclerView.expectDraw(1);
setRecyclerView(recyclerView);
mLayoutManager.waitForLayout(2);
recyclerView.waitForDraw(1);
mLayoutManager.mOnLayoutCallbacks.reset();
getInstrumentation().waitForIdleSync();
assertEquals("extra layouts should not happen", 1, mLayoutManager.getTotalLayoutCount());
assertEquals("all expected children should be laid out", firstLayoutItemCount,
mLayoutManager.getChildCount());
return recyclerView;
| public void | testAdapterChangeDuringScrolling()
setupBasic(10);
final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
final AtomicInteger onScrollItemCount = new AtomicInteger(0);
mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
@Override
void onLayoutChildren(RecyclerView.Recycler recycler,
AnimationLayoutManager lm, RecyclerView.State state) {
onLayoutItemCount.set(state.getItemCount());
super.onLayoutChildren(recycler, lm, state);
}
@Override
public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
onScrollItemCount.set(state.getItemCount());
super.onScroll(dx, recycler, state);
}
});
runTestOnUiThread(new Runnable() {
@Override
public void run() {
mTestAdapter.mItems.remove(5);
mTestAdapter.notifyItemRangeRemoved(5, 1);
mRecyclerView.scrollBy(0, 100);
assertTrue("scrolling while there are pending adapter updates should "
+ "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
assertEquals("scroll by should be called w/ updated adapter count",
mTestAdapter.mItems.size(), onScrollItemCount.get());
}
});
| public void | testAddDelete1()
positionStatesTest(5, 0, 5, new AdapterOps() {
// 0 1 2 3 4
// 0 1 2 a b 3 4
// 0 2 a b 3 4
// 0 c d 2 a b 3 4
// 0 c d 2 a 4
// c d 2 a 4
// pre: 0 1 2 3 4
@Override
void onRun(TestAdapter adapter) throws Throwable {
adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{1, -1},
new int[]{1, 2}, new int[]{5, -2}, new int[]{0, -1});
}
}, PositionConstraint.scrap(0, 0, -1), PositionConstraint.scrap(1, 1, -1),
PositionConstraint.scrap(2, 2, 2), PositionConstraint.scrap(3, 3, -1),
PositionConstraint.scrap(4, 4, 4), PositionConstraint.adapter(0),
PositionConstraint.adapter(1), PositionConstraint.adapter(3)
);
| public void | testAddDelete2()
positionStatesTest(5, 0, 5, new AdapterOps() {
// 0 1 2 3 4
// 0 1 2 a b 3 4
// 0 1 b 3 4
// pre: 0 1 2 3 4
// pre w/ adap: 0 1 2 b 3 4
@Override
void onRun(TestAdapter adapter) throws Throwable {
adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{2, -2});
}
}, PositionConstraint.scrap(2, 2, -1), PositionConstraint.scrap(1, 1, 1),
PositionConstraint.scrap(3, 3, 3)
);
| public void | testAddInvisible()
setupBasic(10, 1, 7);
mLayoutManager.expectLayouts(1);
mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{8, 1});// add a new item 0
mLayoutManager.waitForLayout(2);
| public void | testAddInvisibleAndVisible()
setupBasic(10, 1, 7);
mLayoutManager.expectLayouts(2);
mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{7, 1});// add a new item 0 // invisible
mLayoutManager.waitForLayout(2);
| public void | testAddManyMultiStep()
setupBasic(10, 1, 7);
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
mLayoutManager.expectLayouts(1);
// try to trigger race conditions
int targetItemCount = mTestAdapter.getItemCount();
for (int i = 0; i < 100; i++) {
mTestAdapter.addAndNotify(0, 1);
mTestAdapter.addAndNotify(7, 1);
targetItemCount += 2;
}
// wait until main thread runnables are consumed
while (targetItemCount != mTestAdapter.getItemCount()) {
Thread.sleep(100);
}
mLayoutManager.waitForLayout(2);
| public void | testAddRemoveSamePass()
final List<RecyclerView.ViewHolder> mRecycledViews
= new ArrayList<RecyclerView.ViewHolder>();
TestAdapter adapter = new TestAdapter(50) {
@Override
public void onViewRecycled(TestViewHolder holder) {
super.onViewRecycled(holder);
mRecycledViews.add(holder);
}
};
adapter.setHasStableIds(true);
setupBasic(50, 3, 5, adapter);
mRecyclerView.setItemViewCacheSize(0);
final ArrayList<RecyclerView.ViewHolder> addVH
= new ArrayList<RecyclerView.ViewHolder>();
final ArrayList<RecyclerView.ViewHolder> removeVH
= new ArrayList<RecyclerView.ViewHolder>();
final ArrayList<RecyclerView.ViewHolder> moveVH
= new ArrayList<RecyclerView.ViewHolder>();
final View[] testView = new View[1];
mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
addVH.add(holder);
return true;
}
@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {
removeVH.add(holder);
return true;
}
@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
moveVH.add(holder);
return true;
}
});
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void afterPreLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager layoutManager,
RecyclerView.State state) {
super.afterPreLayout(recycler, layoutManager, state);
testView[0] = recycler.getViewForPosition(45);
testView[0].measure(View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST));
testView[0].layout(10, 10, 10 + testView[0].getMeasuredWidth(),
10 + testView[0].getMeasuredHeight());
layoutManager.addView(testView[0], 4);
}
@Override
void afterPostLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager layoutManager,
RecyclerView.State state) {
super.afterPostLayout(recycler, layoutManager, state);
testView[0].layout(50, 50, 50 + testView[0].getMeasuredWidth(),
50 + testView[0].getMeasuredHeight());
layoutManager.addDisappearingView(testView[0], 4);
}
};
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 3;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 5;
mRecycledViews.clear();
mLayoutManager.expectLayouts(2);
mTestAdapter.deleteAndNotify(3, 1);
mLayoutManager.waitForLayout(2);
for (RecyclerView.ViewHolder vh : addVH) {
assertNotSame("add-remove item should not animate add", testView[0], vh.itemView);
}
for (RecyclerView.ViewHolder vh : moveVH) {
assertNotSame("add-remove item should not animate move", testView[0], vh.itemView);
}
for (RecyclerView.ViewHolder vh : removeVH) {
assertNotSame("add-remove item should not animate remove", testView[0], vh.itemView);
}
boolean found = false;
for (RecyclerView.ViewHolder vh : mRecycledViews) {
found |= vh.itemView == testView[0];
}
assertTrue("added-removed view should be recycled", found);
| public void | testAddSameIndexTwice()
positionStatesTest(12, 2, 7, new AdapterOps() {
@Override
void onRun(TestAdapter adapter) throws Throwable {
adapter.addAndNotify(new int[]{1, 2}, new int[]{5, 1}, new int[]{5, 1},
new int[]{11, 1});
}
}, PositionConstraint.adapterScrap(0, 0), PositionConstraint.adapterScrap(1, 3),
PositionConstraint.scrap(2, 2, 4), PositionConstraint.scrap(3, 3, 7),
PositionConstraint.scrap(4, 4, 8), PositionConstraint.scrap(7, 7, 12),
PositionConstraint.scrap(8, 8, 13)
);
| public void | testBasicAdd()
setupBasic(10);
mLayoutManager.expectLayouts(2);
setExpectedItemCounts(10, 13);
mTestAdapter.addAndNotify(2, 3);
mLayoutManager.waitForLayout(2);
| public void | testBasicDelete()
setupBasic(10);
final OnLayoutCallbacks callbacks = new OnLayoutCallbacks() {
@Override
public void postDispatchLayout() {
// verify this only in first layout
assertEquals("deleted views should still be children of RV",
mLayoutManager.getChildCount() + mDeletedViewCount
, mRecyclerView.getChildCount());
}
@Override
void afterPreLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager layoutManager,
RecyclerView.State state) {
super.afterPreLayout(recycler, layoutManager, state);
mLayoutItemCount = 3;
mLayoutMin = 0;
}
};
callbacks.mLayoutItemCount = 10;
callbacks.setExpectedItemCounts(10, 3);
mLayoutManager.setOnLayoutCallbacks(callbacks);
mLayoutManager.expectLayouts(2);
mTestAdapter.deleteAndNotify(0, 7);
mLayoutManager.waitForLayout(2);
callbacks.reset();// when animations end another layout will happen
| public void | testChangeAnimations()
final boolean[] booleans = {true, false};
for (boolean supportsChange : booleans) {
for (boolean changeType : booleans) {
for (boolean hasStableIds : booleans) {
for (boolean deleteSomeItems : booleans) {
changeAnimTest(supportsChange, changeType, hasStableIds, deleteSomeItems);
}
removeRecyclerView();
}
}
}
| public void | testDeleteInvisible()
setupBasic(10, 1, 7);
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
mLayoutManager.expectLayouts(1);
mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(8, 8);
mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});// delete item id 0,8
mLayoutManager.waitForLayout(2);
| public void | testDeleteInvisibleMultiStep()
setupBasic(1000, 1, 7);
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
mLayoutManager.expectLayouts(1);
// try to trigger race conditions
int targetItemCount = mTestAdapter.getItemCount();
for (int i = 0; i < 100; i++) {
mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});
checkForMainThreadException();
targetItemCount -= 2;
}
// wait until main thread runnables are consumed
while (targetItemCount != mTestAdapter.getItemCount()) {
Thread.sleep(100);
}
mLayoutManager.waitForLayout(2);
| public void | testDeleteTwice()
positionStatesTest(12, 2, 7, new AdapterOps() {
@Override
void onRun(TestAdapter adapter) throws Throwable {
adapter.deleteAndNotify(new int[]{0, 1}, new int[]{1, 1}, new int[]{7, 1},
new int[]{0, 1});// delete item ids 0,2,9,1
}
}, PositionConstraint.scrap(2, 0, -1), PositionConstraint.scrap(3, 1, 0),
PositionConstraint.scrap(4, 2, 1), PositionConstraint.scrap(5, 3, 2),
PositionConstraint.scrap(6, 4, 3), PositionConstraint.scrap(8, 6, 5),
PositionConstraint.adapterScrap(7, 6), PositionConstraint.adapterScrap(8, 7)
);
| public void | testDeleteVisibleAndInvisible()
setupBasic(11, 3, 5); //layout items 3 4 5 6 7
mLayoutManager.expectLayouts(2);
setLayoutRange(3, 5); //layout previously invisible child 10 from end of the list
setExpectedItemCounts(9, 8);
mTestAdapter.deleteAndNotify(new int[]{4, 1}, new int[]{7, 2});// delete items 4, 8, 9
mLayoutManager.waitForLayout(2);
| public void | testDetachBeforeAnimations()
setupBasic(10, 0, 5);
final RecyclerView rv = mRecyclerView;
waitForAnimations(2);
final DefaultItemAnimator animator = new DefaultItemAnimator() {
@Override
public void runPendingAnimations() {
super.runPendingAnimations();
}
};
rv.setItemAnimator(animator);
mLayoutManager.expectLayouts(2);
mTestAdapter.deleteAndNotify(3, 4);
mLayoutManager.waitForLayout(2);
removeRecyclerView();
assertNull("test sanity check RV should be removed", rv.getParent());
assertEquals("no views should be hidden", 0, rv.mChildHelper.mHiddenViews.size());
assertFalse("there should not be any animations running", animator.isRunning());
| public void | testFindPositionOffset()
setupBasic(10);
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void beforePreLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager lm, RecyclerView.State state) {
super.beforePreLayout(recycler, lm, state);
// [0,2,4]
assertEquals("offset check", 0, mAdapterHelper.findPositionOffset(0));
assertEquals("offset check", 1, mAdapterHelper.findPositionOffset(2));
assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4));
}
};
runTestOnUiThread(new Runnable() {
@Override
public void run() {
// [0,1,2,3,4]
// delete 1
mTestAdapter.notifyItemRangeRemoved(1, 1);
// delete 3
mTestAdapter.notifyItemRangeRemoved(2, 1);
}
});
mLayoutManager.waitForLayout(2);
| public void | testGetItemForDeletedView()
getItemForDeletedViewTest(false);
getItemForDeletedViewTest(true);
| public void | testNotifyDataSetChanged()
setupBasic(10, 3, 4);
int layoutCount = mLayoutManager.mTotalLayoutCount;
mLayoutManager.expectLayouts(1);
runTestOnUiThread(new Runnable() {
@Override
public void run() {
try {
mTestAdapter.deleteAndNotify(4, 1);
mTestAdapter.dispatchDataSetChanged();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
});
mLayoutManager.waitForLayout(2);
getInstrumentation().waitForIdleSync();
assertEquals("on notify data set changed, predictive animations should not run",
layoutCount + 1, mLayoutManager.mTotalLayoutCount);
mLayoutManager.expectLayouts(2);
mTestAdapter.addAndNotify(4, 2);
// make sure animations recover
mLayoutManager.waitForLayout(2);
| public void | testNotifyDataSetChangedDuringScroll()
setupBasic(10);
final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
final AtomicInteger onScrollItemCount = new AtomicInteger(0);
mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
@Override
void onLayoutChildren(RecyclerView.Recycler recycler,
AnimationLayoutManager lm, RecyclerView.State state) {
onLayoutItemCount.set(state.getItemCount());
super.onLayoutChildren(recycler, lm, state);
}
@Override
public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
onScrollItemCount.set(state.getItemCount());
super.onScroll(dx, recycler, state);
}
});
runTestOnUiThread(new Runnable() {
@Override
public void run() {
mTestAdapter.mItems.remove(5);
mTestAdapter.notifyDataSetChanged();
mRecyclerView.scrollBy(0, 100);
assertTrue("scrolling while there are pending adapter updates should "
+ "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
assertEquals("scroll by should be called w/ updated adapter count",
mTestAdapter.mItems.size(), onScrollItemCount.get());
}
});
| public void | testPreLayoutPositionCleanup()
setupBasic(4, 0, 4);
mLayoutManager.expectLayouts(2);
mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
@Override
void beforePreLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager lm, RecyclerView.State state) {
mLayoutMin = 0;
mLayoutItemCount = 3;
}
@Override
void beforePostLayout(RecyclerView.Recycler recycler,
AnimationLayoutManager layoutManager,
RecyclerView.State state) {
mLayoutMin = 0;
mLayoutItemCount = 4;
}
};
mTestAdapter.addAndNotify(0, 1);
mLayoutManager.waitForLayout(2);
| public void | testRecycleDuringAnimations()
final AtomicInteger childCount = new AtomicInteger(0);
final TestAdapter adapter = new TestAdapter(1000) {
@Override
public TestViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
childCount.incrementAndGet();
return super.onCreateViewHolder(parent, viewType);
}
};
setupBasic(1000, 10, 20, adapter);
mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 10;
mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 20;
mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() {
@Override
public void putRecycledView(RecyclerView.ViewHolder scrap) {
super.putRecycledView(scrap);
childCount.decrementAndGet();
}
@Override
public RecyclerView.ViewHolder getRecycledView(int viewType) {
final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
if (recycledView != null) {
childCount.incrementAndGet();
}
return recycledView;
}
});
// now keep adding children to trigger more children being created etc.
for (int i = 0; i < 100; i ++) {
adapter.addAndNotify(15, 1);
Thread.sleep(50);
}
getInstrumentation().waitForIdleSync();
waitForAnimations(2);
assertEquals("Children count should add up", childCount.get(),
mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
| public void | testRemoveScrapInvalidate()
setupBasic(10);
TestRecyclerView testRecyclerView = getTestRecyclerView();
mLayoutManager.expectLayouts(1);
testRecyclerView.expectDraw(1);
runTestOnUiThread(new Runnable() {
@Override
public void run() {
mTestAdapter.mItems.clear();
mTestAdapter.notifyDataSetChanged();
}
});
mLayoutManager.waitForLayout(2);
testRecyclerView.waitForDraw(2);
| public void | testStableIdNotifyDataSetChanged()
final int itemCount = 20;
List<Item> initialSet = new ArrayList<Item>();
final TestAdapter adapter = new TestAdapter(itemCount) {
@Override
public long getItemId(int position) {
return mItems.get(position).mId;
}
};
adapter.setHasStableIds(true);
initialSet.addAll(adapter.mItems);
positionStatesTest(itemCount, 5, 5, adapter, new AdapterOps() {
@Override
void onRun(TestAdapter testAdapter) throws Throwable {
Item item5 = adapter.mItems.get(5);
Item item6 = adapter.mItems.get(6);
item5.mAdapterIndex = 6;
item6.mAdapterIndex = 5;
adapter.mItems.remove(5);
adapter.mItems.add(6, item5);
adapter.dispatchDataSetChanged();
//hacky, we support only 1 layout pass
mLayoutManager.layoutLatch.countDown();
}
}, PositionConstraint.scrap(6, -1, 5), PositionConstraint.scrap(5, -1, 6),
PositionConstraint.scrap(7, -1, 7), PositionConstraint.scrap(8, -1, 8),
PositionConstraint.scrap(9, -1, 9));
// now mix items.
|
|