RecyclerViewAnimationsTest.java revision 7c7fba8365684e1ccfc4f39f286df4d100c6c81f
1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/* 2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project 3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License"); 5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License. 6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at 7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * http://www.apache.org/licenses/LICENSE-2.0 9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software 11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS, 12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and 14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License. 15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */ 16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget; 18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 19b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.content.Context; 20888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyarimport android.graphics.Canvas; 21b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.AttributeSet; 22b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.Log; 23b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.view.View; 24504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 25b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 2611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.ArrayList; 278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.HashMap; 2811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.HashSet; 2911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.List; 3011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Map; 3111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Set; 32888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyarimport java.util.concurrent.CountDownLatch; 33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 34d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 36d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepublic class RecyclerViewAnimationsTest extends BaseRecyclerViewInstrumentationTest { 37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 38668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private static final boolean DEBUG = false; 39b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 40b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private static final String TAG = "RecyclerViewAnimationsTest"; 41b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 42b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar AnimationLayoutManager mLayoutManager; 43b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 44b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar TestAdapter mTestAdapter; 45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public RecyclerViewAnimationsTest() { 47b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(DEBUG); 48b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected void setUp() throws Exception { 52b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.setUp(); 53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView setupBasic(int itemCount) throws Throwable { 56b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return setupBasic(itemCount, 0, itemCount); 57b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount) 60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar throws Throwable { 6111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar return setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null); 6211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 6311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 6411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount, 6511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar TestAdapter testAdapter) 6611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar throws Throwable { 67888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar final TestRecyclerView recyclerView = new TestRecyclerView(getActivity()); 68b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar recyclerView.setHasFixedSize(true); 6911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (testAdapter == null) { 7011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mTestAdapter = new TestAdapter(itemCount); 7111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } else { 7211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mTestAdapter = testAdapter; 7311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 74b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar recyclerView.setAdapter(mTestAdapter); 75b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager = new AnimationLayoutManager(); 76b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar recyclerView.setLayoutManager(mLayoutManager); 77b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = firstLayoutStartIndex; 78b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = firstLayoutItemCount; 79b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 80b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(1); 81888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar recyclerView.expectDraw(1); 82b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setRecyclerView(recyclerView); 83b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 84888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar recyclerView.waitForDraw(1); 85888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mLayoutManager.mOnLayoutCallbacks.reset(); 868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 87b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar assertEquals("extra layouts should not happen", 1, mLayoutManager.getTotalLayoutCount()); 888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("all expected children should be laid out", firstLayoutItemCount, 898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.getChildCount()); 90b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return recyclerView; 91b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 92b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 937c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void testChangeAnimations() throws Throwable { 947c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final boolean[] booleans = {true, false}; 957c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean supportsChange : booleans) { 967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean changeType : booleans) { 977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean hasStableIds : booleans) { 987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen for (boolean deleteSomeItems : booleans) { 997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen changeAnimTest(supportsChange, changeType, hasStableIds, deleteSomeItems); 1007c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1017c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen removeRecyclerView(); 1027c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1037c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1047c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1057c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1067c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void changeAnimTest(final boolean supportsChangeAnim, final boolean changeType, 1077c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final boolean hasStableIds, final boolean deleteSomeItems) throws Throwable { 1087c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final int changedIndex = 3; 1097c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final int defaultType = 1; 1107c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final AtomicInteger changedIndexNewType = new AtomicInteger(defaultType); 1117c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final String logPrefix = "supportsChangeAnim:" + supportsChangeAnim + 1127c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen ", change view type:" + changeType + 1137c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen ", has stable ids:" + hasStableIds + 1147c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen ", force predictive:" + deleteSomeItems; 1157c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen TestAdapter testAdapter = new TestAdapter(10) { 1167c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 1177c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public int getItemViewType(int position) { 1187c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen return position == changedIndex ? changedIndexNewType.get() : defaultType; 1197c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1207c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 1217c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 1227c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public TestViewHolder onCreateViewHolder(ViewGroup parent, 1237c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen int viewType) { 1247c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen TestViewHolder vh = super.onCreateViewHolder(parent, viewType); 1257c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (DEBUG) { 1267c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen Log.d(TAG, logPrefix + " onCreateVH" + vh.toString()); 1277c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen return vh; 1297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 1317c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 1327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void onBindViewHolder(TestViewHolder holder, 1337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen int position) { 1347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen super.onBindViewHolder(holder, position); 1357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (DEBUG) { 1367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen Log.d(TAG, logPrefix + " onBind to " + position + "" + holder.toString()); 1377c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1387c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1397c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen }; 1407c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen testAdapter.setHasStableIds(hasStableIds); 1417c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter); 1427c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mRecyclerView.getItemAnimator().setSupportsChangeAnimations(supportsChangeAnim); 1437c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 1447c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen final RecyclerView.ViewHolder toBeChangedVH = 1457c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mRecyclerView.findViewHolderForPosition(changedIndex); 1467c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 1477c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 1487c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen void afterPreLayout(RecyclerView.Recycler recycler, 1497c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen AnimationLayoutManager layoutManager, 1507c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen RecyclerView.State state) { 1517c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForPosition(changedIndex); 1527c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (supportsChangeAnim) { 1537c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertTrue(logPrefix + " changed view holder should have correct flag" 1547c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen , vh.isChanged()); 1557c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } else { 1567c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertFalse(logPrefix + " changed view holder should have correct flag" 1577c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen , vh.isChanged()); 1587c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1597c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1607c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 1617c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 1627c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen void afterPostLayout(RecyclerView.Recycler recycler, 1637c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen AnimationLayoutManager layoutManager, RecyclerView.State state) { 1647c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForPosition(changedIndex); 1657c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertFalse(logPrefix + "VH should not be marked as changed", vh.isChanged()); 1667c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (supportsChangeAnim) { 1677c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertNotSame(logPrefix + "a new VH should be given if change is supported", 1687c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen toBeChangedVH, vh); 1697c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } else if (!changeType && hasStableIds) { 1707c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen assertSame(logPrefix + "if change animations are not supported but we have " 1717c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen + "stable ids, same view holder should be returned", toBeChangedVH, vh); 1727c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1737c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen super.beforePostLayout(recycler, layoutManager, state); 1747c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1757c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen }; 1767c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mLayoutManager.expectLayouts(1); 1777c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (changeType) { 1787c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen changedIndexNewType.set(defaultType + 1); 1797c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1807c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen if (deleteSomeItems) { 1817c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen runTestOnUiThread(new Runnable() { 1827c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen @Override 1837c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen public void run() { 1847c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen try { 1857c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mTestAdapter.deleteAndNotify(changedIndex + 2, 1); 1867c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mTestAdapter.notifyItemChanged(3); 1877c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } catch (Throwable throwable) { 1887c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen throwable.printStackTrace(); 1897c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1907c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 1917c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1927c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen }); 1937c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } else { 1947c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mTestAdapter.notifyItemChanged(3); 1957c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 1977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen mLayoutManager.waitForLayout(2); 1987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen } 1997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen 200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(1000) { 203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar childCount.incrementAndGet(); 207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return super.onCreateViewHolder(parent, viewType); 208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar setupBasic(1000, 10, 20, adapter); 211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 10; 212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 20; 213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() { 215504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 216504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 217504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 218504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar childCount.decrementAndGet(); 219504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 220504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 221504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 222504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 223504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 224504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 225504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar childCount.incrementAndGet(); 226504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 227504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 228504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 229504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 230504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 231504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 232504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 233504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 234504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(50); 235504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 236504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 237504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 238504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 239504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 240504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 241504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 242b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar public void testNotifyDataSetChanged() throws Throwable { 243b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar setupBasic(10, 3, 4); 244b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar int layoutCount = mLayoutManager.mTotalLayoutCount; 245b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.expectLayouts(1); 246b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar runTestOnUiThread(new Runnable() { 247b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar @Override 248b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar public void run() { 249b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar try { 250b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mTestAdapter.deleteAndNotify(4, 1); 251cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase mTestAdapter.dispatchDataSetChanged(); 252b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } catch (Throwable throwable) { 253b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar throwable.printStackTrace(); 254b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } 255b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar 256b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } 257b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar }); 258b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.waitForLayout(2); 259b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar getInstrumentation().waitForIdleSync(); 260b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar assertEquals("on notify data set changed, predictive animations should not run", 261b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar layoutCount + 1, mLayoutManager.mTotalLayoutCount); 262b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.expectLayouts(2); 263b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mTestAdapter.addAndNotify(4, 2); 264b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar // make sure animations recover 265b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar mLayoutManager.waitForLayout(2); 266b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar } 267b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar 268e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void testStableIdNotifyDataSetChanged() throws Throwable { 269e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final int itemCount = 20; 270e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar List<Item> initialSet = new ArrayList<Item>(); 271e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final TestAdapter adapter = new TestAdapter(itemCount) { 272e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 273e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public long getItemId(int position) { 274e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar return mItems.get(position).mId; 275e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 276e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }; 277e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapter.setHasStableIds(true); 278e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar initialSet.addAll(adapter.mItems); 279e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar positionStatesTest(itemCount, 5, 5, adapter, new AdapterOps() { 280e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 281e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar void onRun(TestAdapter testAdapter) throws Throwable { 282e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item5 = adapter.mItems.get(5); 283e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item6 = adapter.mItems.get(6); 284e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item5.mAdapterIndex = 6; 285e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item6.mAdapterIndex = 5; 286e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapter.mItems.remove(5); 287e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapter.mItems.add(6, item5); 288cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase adapter.dispatchDataSetChanged(); 289e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar //hacky, we support only 1 layout pass 290e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mLayoutManager.layoutLatch.countDown(); 291e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 292e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }, PositionConstraint.scrap(6, -1, 5), PositionConstraint.scrap(5, -1, 6), 293e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar PositionConstraint.scrap(7, -1, 7), PositionConstraint.scrap(8, -1, 8), 294e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar PositionConstraint.scrap(9, -1, 9)); 295e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar // now mix items. 296e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 297e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 298d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 2998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testGetItemForDeletedView() throws Throwable { 3008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getItemForDeletedViewTest(false); 3018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getItemForDeletedViewTest(true); 30211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 30311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 3048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void getItemForDeletedViewTest(boolean stableIds) throws Throwable { 30511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final Set<Integer> itemViewTypeQueries = new HashSet<Integer>(); 30611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final Set<Integer> itemIdQueries = new HashSet<Integer>(); 30711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar TestAdapter adapter = new TestAdapter(10) { 30811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar @Override 30911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar public int getItemViewType(int position) { 31011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.add(position); 31111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar return super.getItemViewType(position); 31211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 31311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 31411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar @Override 31511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar public long getItemId(int position) { 31611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.add(position); 31711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar return mItems.get(position).mId; 31811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 31911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar }; 32011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar adapter.setHasStableIds(stableIds); 32111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar setupBasic(10, 0, 10, adapter); 32211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertEquals("getItemViewType for all items should be called", 10, 32311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.size()); 32411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (adapter.hasStableIds()) { 32511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertEquals("getItemId should be called when adapter has stable ids", 10, 32611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.size()); 32711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } else { 32811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertEquals("getItemId should not be called when adapter does not have stable ids", 0, 32911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.size()); 33011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 33111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.clear(); 33211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.clear(); 33311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mLayoutManager.expectLayouts(2); 33411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar // delete last two 33511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final int deleteStart = 8; 33611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final int deleteCount = adapter.getItemCount() - deleteStart; 33711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar adapter.deleteAndNotify(deleteStart, deleteCount); 33811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar mLayoutManager.waitForLayout(2); 33911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar for (int i = 0; i < deleteStart; i++) { 34011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertTrue("getItemViewType for existing item " + i + " should be called", 34111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.contains(i)); 34211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (adapter.hasStableIds()) { 34311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertTrue("getItemId for existing item " + i 34411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar + " should be called when adapter has stable ids", 34511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.contains(i)); 34611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 34711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 34811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar for (int i = deleteStart; i < deleteStart + deleteCount; i++) { 34911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar assertFalse("getItemViewType for deleted item " + i + " SHOULD NOT be called", 35011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemViewTypeQueries.contains(i)); 35111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar if (adapter.hasStableIds()) { 3528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertFalse("getItemId for deleted item " + i + " SHOULD NOT be called", 35311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar itemIdQueries.contains(i)); 35411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 35511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 35611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar } 35711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 3588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testDeleteInvisibleMultiStep() throws Throwable { 3598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(1000, 1, 7); 3608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1; 3618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7; 3628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.expectLayouts(1); 3638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // try to trigger race conditions 3648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int targetItemCount = mTestAdapter.getItemCount(); 3658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < 100; i++) { 3668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1}); 3678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar targetItemCount -= 2; 3688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // wait until main thread runnables are consumed 3708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar while (targetItemCount != mTestAdapter.getItemCount()) { 3718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(100); 3728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.waitForLayout(2); 3748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddManyMultiStep() throws Throwable { 3778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(10, 1, 7); 3788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1; 3798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7; 3808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.expectLayouts(1); 3818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // try to trigger race conditions 3828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int targetItemCount = mTestAdapter.getItemCount(); 3838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < 100; i++) { 3848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.addAndNotify(0, 1); 3858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.addAndNotify(7, 1); 3868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar targetItemCount += 2; 3878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // wait until main thread runnables are consumed 3898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar while (targetItemCount != mTestAdapter.getItemCount()) { 3908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(100); 3918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.waitForLayout(2); 3938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 3948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testBasicDelete() throws Throwable { 3968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(10); 3978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final OnLayoutCallbacks callbacks = new OnLayoutCallbacks() { 3988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 3998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void postDispatchLayout() { 4008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // verify this only in first layout 4018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("deleted views should still be children of RV", 4028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.getChildCount() + mDeletedViewCount 4038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar , mRecyclerView.getChildCount()); 4048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 4078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void afterPreLayout(RecyclerView.Recycler recycler, 4088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar AnimationLayoutManager layoutManager, 4098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.State state) { 4108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.afterPreLayout(recycler, layoutManager, state); 4118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutItemCount = 3; 4128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutMin = 0; 4138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }; 4158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar callbacks.mLayoutItemCount = 10; 4168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar callbacks.setExpectedItemCounts(10, 3); 4178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.setOnLayoutCallbacks(callbacks); 4188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.expectLayouts(2); 4208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.deleteAndNotify(0, 7); 4218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.waitForLayout(2); 4228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar callbacks.reset();// when animations end another layout will happen 4238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 426d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void testAdapterChangeDuringScrolling() throws Throwable { 427d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar setupBasic(10); 428d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar final AtomicInteger onLayoutItemCount = new AtomicInteger(0); 429d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar final AtomicInteger onScrollItemCount = new AtomicInteger(0); 430d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 431d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() { 432d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 433d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar void onLayoutChildren(RecyclerView.Recycler recycler, 434d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 435d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar onLayoutItemCount.set(state.getItemCount()); 436d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar super.onLayoutChildren(recycler, lm, state); 437d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 438d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 439d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 440d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 441d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar onScrollItemCount.set(state.getItemCount()); 442d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar super.onScroll(dx, recycler, state); 443d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 444d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar }); 445d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar runTestOnUiThread(new Runnable() { 446d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 447d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void run() { 448d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mTestAdapter.mItems.remove(5); 449d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mTestAdapter.notifyItemRangeRemoved(5, 1); 450d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mRecyclerView.scrollBy(0, 100); 451d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar assertTrue("scrolling while there are pending adapter updates should " 452d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0); 453d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar assertEquals("scroll by should be called w/ updated adapter count", 454d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mTestAdapter.mItems.size(), onScrollItemCount.get()); 455d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 456d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 457d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar }); 458d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 459d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 460b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testAddInvisibleAndVisible() throws Throwable { 461b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10, 1, 7); 462b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 463b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12); 4642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{7, 1});// add a new item 0 // invisible 465b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 466b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 467b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 468b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testAddInvisible() throws Throwable { 469b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10, 1, 7); 470b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(1); 471b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12); 4722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{8, 1});// add a new item 0 473b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 474b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 475b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 476b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testBasicAdd() throws Throwable { 477b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10); 478b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 479b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setExpectedItemCounts(10, 13); 480b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mTestAdapter.addAndNotify(2, 3); 481b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 482b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 483b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 484888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public TestRecyclerView getTestRecyclerView() { 485888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar return (TestRecyclerView) mRecyclerView; 486888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 487888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 488888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void testRemoveScrapInvalidate() throws Throwable { 489888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar setupBasic(10); 490888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar TestRecyclerView testRecyclerView = getTestRecyclerView(); 491888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mLayoutManager.expectLayouts(1); 492888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar testRecyclerView.expectDraw(1); 493888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar runTestOnUiThread(new Runnable() { 494888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar @Override 495888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void run() { 496888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mTestAdapter.mItems.clear(); 497888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mTestAdapter.notifyDataSetChanged(); 498888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 499888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar }); 500888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar mLayoutManager.waitForLayout(2); 501888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar testRecyclerView.waitForDraw(2); 502888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 503888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 504b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testDeleteVisibleAndInvisible() throws Throwable { 505b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(11, 3, 5); //layout items 3 4 5 6 7 506b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 5078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setLayoutRange(3, 5); //layout previously invisible child 10 from end of the list 508b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setExpectedItemCounts(9, 8); 509b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mTestAdapter.deleteAndNotify(new int[]{4, 1}, new int[]{7, 2});// delete items 4, 8, 9 510b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 511b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 512b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 5138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testFindPositionOffset() throws Throwable { 5148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar setupBasic(10); 5158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(new Runnable() { 5168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 5188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // [0,1,2,3,4] 5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // delete 1 5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.notifyItemRangeRemoved(1, 1); 5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // delete 3 5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.notifyItemRangeRemoved(2, 1); 5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper.preProcess(); 5248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // [0,2,4] 5258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("offset check", 0, mAdapterHelper.findPositionOffset(0)); 5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("offset check", 1, mAdapterHelper.findPositionOffset(2)); 5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4)); 5288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }); 5318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 533b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private void setLayoutRange(int start, int count) { 534b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = start; 535b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = count; 536b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 537b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 538b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private void setExpectedItemCounts(int preLayout, int postLayout) { 539b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(preLayout, postLayout); 540b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 541b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 542b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void testDeleteInvisible() throws Throwable { 543b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar setupBasic(10, 1, 7); 5448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1; 5458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7; 546b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(1); 547b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(8, 8); 5488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});// delete item id 0,8 549b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 550b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 551b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 552e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar private CollectPositionResult findByPos(RecyclerView recyclerView, 553e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state, int position) { 554e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar View view = recycler.getViewForPosition(position, true); 555e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 556e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (vh.wasReturnedFromScrap()) { 557e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar vh.clearReturnedFromScrapFlag(); //keep data consistent. 558e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar return CollectPositionResult.fromScrap(vh); 559e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } else { 560e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar return CollectPositionResult.fromAdapter(vh); 5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 564e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public Map<Integer, CollectPositionResult> collectPositions(RecyclerView recyclerView, 565e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state, int... positions) { 5668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Map<Integer, CollectPositionResult> positionToAdapterMapping 5678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar = new HashMap<Integer, CollectPositionResult>(); 5688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int position : positions) { 5698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (position < 0) { 5708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar continue; 5718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 572e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar positionToAdapterMapping.put(position, 573e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar findByPos(recyclerView, recycler, state, position)); 5748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return positionToAdapterMapping; 5768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddDelete2() throws Throwable { 5798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(5, 0, 5, new AdapterOps() { 5808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 3 4 5818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 a b 3 4 5828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 b 3 4 5838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // pre: 0 1 2 3 4 5848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // pre w/ adap: 0 1 2 b 3 4 585d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 5868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 5878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{2, -2}); 588b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 5898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.scrap(2, 2, -1), PositionConstraint.scrap(1, 1, 1), 5908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(3, 3, 3) 5918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 5928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 593d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddDelete1() throws Throwable { 5958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(5, 0, 5, new AdapterOps() { 5968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 3 4 5978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 1 2 a b 3 4 5988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 2 a b 3 4 5998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 c d 2 a b 3 4 6008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // 0 c d 2 a 4 6018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // c d 2 a 4 6028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // pre: 0 1 2 3 4 603b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 6048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 6058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{1, -1}, 6068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new int[]{1, 2}, new int[]{5, -2}, new int[]{0, -1}); 607d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 6088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.scrap(0, 0, -1), PositionConstraint.scrap(1, 1, -1), 6098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(2, 2, 2), PositionConstraint.scrap(3, 3, -1), 6108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(4, 4, 4), PositionConstraint.adapter(0), 6118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.adapter(1), PositionConstraint.adapter(3) 6128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 6138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testAddSameIndexTwice() throws Throwable { 6168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(12, 2, 7, new AdapterOps() { 6178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 6198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.addAndNotify(new int[]{1, 2}, new int[]{5, 1}, new int[]{5, 1}, 6208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new int[]{11, 1}); 6218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.adapterScrap(0, 0), PositionConstraint.adapterScrap(1, 3), 6238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(2, 2, 4), PositionConstraint.scrap(3, 3, 7), 6248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(4, 4, 8), PositionConstraint.scrap(7, 7, 12), 6258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(8, 8, 13) 6268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 6278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void testDeleteTwice() throws Throwable { 6308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar positionStatesTest(12, 2, 7, new AdapterOps() { 6318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void onRun(TestAdapter adapter) throws Throwable { 6338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter.deleteAndNotify(new int[]{0, 1}, new int[]{1, 1}, new int[]{7, 1}, 6348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new int[]{0, 1});// delete item ids 0,2,9,1 6358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }, PositionConstraint.scrap(2, 0, -1), PositionConstraint.scrap(3, 1, 0), 6378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(4, 2, 1), PositionConstraint.scrap(5, 3, 2), 6388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.scrap(6, 4, 3), PositionConstraint.scrap(8, 6, 5), 6398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint.adapterScrap(7, 6), PositionConstraint.adapterScrap(8, 7) 6408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ); 6418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 6448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void positionStatesTest(int itemCount, int firstLayoutStartIndex, 645e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int firstLayoutItemCount, AdapterOps adapterChanges, 646e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final PositionConstraint... constraints) throws Throwable { 647e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar positionStatesTest(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null, 648e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar adapterChanges, constraints); 649e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 650e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void positionStatesTest(int itemCount, int firstLayoutStartIndex, 651e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int firstLayoutItemCount,TestAdapter adapter, AdapterOps adapterChanges, 652e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar final PositionConstraint... constraints) throws Throwable { 653e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, adapter); 654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.expectLayouts(2); 6558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() { 6568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void beforePreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 6588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.State state) { 6598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.beforePreLayout(recycler, lm, state); 6608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar //harmless 6618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.detachAndScrapAttachedViews(recycler); 6628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[] ids = new int[constraints.length]; 6638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < constraints.length; i++) { 6648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ids[i] = constraints[i].mPreLayoutPos; 6658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Map<Integer, CollectPositionResult> positions 667e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar = collectPositions(lm.mRecyclerView, recycler, state, ids); 6688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (PositionConstraint constraint : constraints) { 6698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (constraint.mPreLayoutPos != -1) { 6708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.validate(state, positions.get(constraint.mPreLayoutPos), 6718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.getLog()); 6728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 6788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.State state) { 6798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.beforePostLayout(recycler, lm, state); 6808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.detachAndScrapAttachedViews(recycler); 6818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[] ids = new int[constraints.length]; 6828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < constraints.length; i++) { 6838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ids[i] = constraints[i].mPostLayoutPos; 6848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Map<Integer, CollectPositionResult> positions 686e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar = collectPositions(lm.mRecyclerView, recycler, state, ids); 6878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (PositionConstraint constraint : constraints) { 6888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (constraint.mPostLayoutPos >= 0) { 6898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.validate(state, positions.get(constraint.mPostLayoutPos), 6908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.getLog()); 6918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }; 6958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapterChanges.run(mTestAdapter); 696b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutManager.waitForLayout(2); 697e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar checkForMainThreadException(); 6988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (PositionConstraint constraint : constraints) { 6998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.assertValidate(); 7008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 701b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 702b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 703b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar class AnimationLayoutManager extends TestLayoutManager { 704b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 7058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private int mTotalLayoutCount = 0; 7068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private String log; 7078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 708b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar OnLayoutCallbacks mOnLayoutCallbacks = new OnLayoutCallbacks() { 709b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }; 710b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 7118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 713b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 714c35968d173f900d8024bdf38174e2225c9a7f311Chet Haase public boolean supportsPredictiveItemAnimations() { 715b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return true; 716b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 717b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 7188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String getLog() { 7198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return log; 7208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 722668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private String prepareLog(RecyclerView.Recycler recycler, RecyclerView.State state, boolean done) { 7238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar StringBuilder builder = new StringBuilder(); 724668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar builder.append("is pre layout:").append(state.isPreLayout()).append(", done:").append(done); 725668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar builder.append("\nViewHolders:\n"); 7268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (RecyclerView.ViewHolder vh : ((TestRecyclerView)mRecyclerView).collectViewHolders()) { 7278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar builder.append(vh).append("\n"); 7288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar builder.append("scrap:\n"); 7308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (RecyclerView.ViewHolder vh : recycler.getScrapList()) { 7318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar builder.append(vh).append("\n"); 7328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 733668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar 734668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (state.isPreLayout() && !done) { 7358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar log = "\n" + builder.toString(); 7368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 7378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar log += "\n" + builder.toString(); 7388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return log; 7408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 742b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 743b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void expectLayouts(int count) { 744b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.expectLayouts(count); 745b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks.mLayoutCount = 0; 746b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 747b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 748b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setOnLayoutCallbacks(OnLayoutCallbacks onLayoutCallbacks) { 749b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks = onLayoutCallbacks; 750b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 751b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 752b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 753b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public final void onLayoutChildren(RecyclerView.Recycler recycler, 754b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 755b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar try { 7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mTotalLayoutCount++; 757668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar prepareLog(recycler, state, false); 758668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (state.isPreLayout()) { 759668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar validateOldPositions(recycler, state); 760668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } else { 761668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar validateClearedOldPositions(recycler, state); 762668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 763b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks.onLayoutChildren(recycler, this, state); 764668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar prepareLog(recycler, state, true); 765b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } finally { 766b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.countDown(); 767d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 768b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 769b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 770668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private void validateClearedOldPositions(RecyclerView.Recycler recycler, 771668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.State state) { 772668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (getTestRecyclerView() == null) { 773668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar return; 774668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 775668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) { 776668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertEquals("there should NOT be an old position in post layout", 777668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.NO_POSITION, viewHolder.mOldPosition); 778668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertEquals("there should NOT be a pre layout position in post layout", 779668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.NO_POSITION, viewHolder.mPreLayoutPosition); 780668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 781668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 782668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar 783668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar private void validateOldPositions(RecyclerView.Recycler recycler, 784668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar RecyclerView.State state) { 785668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (getTestRecyclerView() == null) { 786668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar return; 787668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 788668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) { 789668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (!viewHolder.isRemoved() && !viewHolder.isInvalid()) { 790668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertTrue("there should be an old position in pre-layout", 791668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar viewHolder.mOldPosition != RecyclerView.NO_POSITION); 792668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 793668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 794668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 795668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar 7968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public int getTotalLayoutCount() { 7978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return mTotalLayoutCount; 7988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 800d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 801d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public boolean canScrollVertically() { 802d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar return true; 803d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 804d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 805d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar @Override 806d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 807d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar RecyclerView.State state) { 808d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar mOnLayoutCallbacks.onScroll(dy, recycler, state); 809d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar return super.scrollVerticallyBy(dy, recycler, state); 810d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 811b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 812b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void onPostDispatchLayout() { 813b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mOnLayoutCallbacks.postDispatchLayout(); 814b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 815b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 816b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 817b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 818b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.waitForLayout(timeout, timeUnit); 819b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar checkForMainThreadException(); 820b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 821d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 822d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 823b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar abstract class OnLayoutCallbacks { 824b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 825b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int mLayoutMin = Integer.MIN_VALUE; 826b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 827b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int mLayoutItemCount = Integer.MAX_VALUE; 828b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 829b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int expectedPreLayoutItemCount = -1; 830b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 831b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int expectedPostLayoutItemCount = -1; 832b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 833b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar int mDeletedViewCount; 834b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 8358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mLayoutCount = 0; 8368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 837b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void setExpectedItemCounts(int preLayout, int postLayout) { 838b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPreLayoutItemCount = preLayout; 839b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPostLayoutItemCount = postLayout; 840b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 841b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 842b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void reset() { 843b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutMin = Integer.MIN_VALUE; 844b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutItemCount = Integer.MAX_VALUE; 845b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPreLayoutItemCount = -1; 846b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPostLayoutItemCount = -1; 8478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mLayoutCount = 0; 848b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 849b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 850b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void beforePreLayout(RecyclerView.Recycler recycler, 851b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar AnimationLayoutManager lm, RecyclerView.State state) { 852b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDeletedViewCount = 0; 853b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar for (int i = 0; i < lm.getChildCount(); i++) { 854b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar View v = lm.getChildAt(i); 855b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (lm.getLp(v).isItemRemoved()) { 856b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDeletedViewCount++; 857b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 858b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 859b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 860b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 861b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void doLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 862b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 863b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (DEBUG) { 864b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar Log.d(TAG, "item count " + state.getItemCount()); 865b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 866b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar lm.detachAndScrapAttachedViews(recycler); 867b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar final int start = mLayoutMin == Integer.MIN_VALUE ? 0 : mLayoutMin; 868b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar final int count = mLayoutItemCount 869b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar == Integer.MAX_VALUE ? state.getItemCount() : mLayoutItemCount; 870c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar lm.layoutRange(recycler, start, start + count); 871b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals("correct # of children should be laid out", 872c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar count, lm.getChildCount()); 8738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar lm.assertVisibleItemPositions(); 874b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 875b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 876b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void onLayoutChildren(RecyclerView.Recycler recycler, AnimationLayoutManager lm, 877b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 878b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 8798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (state.isPreLayout()) { 880b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (expectedPreLayoutItemCount != -1) { 881b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals("on pre layout, state should return abstracted adapter size", 882b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPreLayoutItemCount, state.getItemCount()); 883b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 884b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar beforePreLayout(recycler, lm, state); 8858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 886b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (expectedPostLayoutItemCount != -1) { 887b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals("on post layout, state should return real adapter size", 888b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar expectedPostLayoutItemCount, state.getItemCount()); 889b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 890b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar beforePostLayout(recycler, lm, state); 891b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 892b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar doLayout(recycler, lm, state); 8938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (state.isPreLayout()) { 894b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar afterPreLayout(recycler, lm, state); 8958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 896b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar afterPostLayout(recycler, lm, state); 897b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 898b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mLayoutCount++; 899b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 900b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 901b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void afterPreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager, 902b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 903b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 904b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 905b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager, 906b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 907b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 908b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 909b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void afterPostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager, 910b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.State state) { 911b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 912b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 913b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void postDispatchLayout() { 914b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 915b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 916d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 917d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar 918d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar } 919b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 920b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 921b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar class TestRecyclerView extends RecyclerView { 92211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar 923888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar CountDownLatch drawLatch; 924b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 925b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public TestRecyclerView(Context context) { 926b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(context); 927b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 928b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 929b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public TestRecyclerView(Context context, AttributeSet attrs) { 930b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(context, attrs); 931b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 932b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 933b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public TestRecyclerView(Context context, AttributeSet attrs, int defStyle) { 934b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super(context, attrs, defStyle); 935b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 936b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 9378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 9388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void initAdapterManager() { 9398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.initAdapterManager(); 9408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper.mOnItemProcessedCallback = new Runnable() { 9418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 9428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 9438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validatePostUpdateOp(); 9448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar }; 9468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 948888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void expectDraw(int count) { 949888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar drawLatch = new CountDownLatch(count); 950888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 951888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 952888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar public void waitForDraw(long timeout) throws Throwable { 953888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar drawLatch.await(timeout * (DEBUG ? 100 : 1), TimeUnit.SECONDS); 954888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar assertEquals("all expected draws should happen at the expected time frame", 955888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 0, drawLatch.getCount()); 956888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 957888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 9588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar List<ViewHolder> collectViewHolders() { 9598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar List<ViewHolder> holders = new ArrayList<ViewHolder>(); 9608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int childCount = getChildCount(); 9618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < childCount; i++) { 9628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ViewHolder holder = getChildViewHolderInt(getChildAt(i)); 9638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (holder != null) { 9648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar holders.add(holder); 9658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return holders; 9688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 9708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 9718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void validateViewHolderPositions() { 9728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final Set<Integer> existingOffsets = new HashSet<Integer>(); 973668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar int childCount = getChildCount(); 974668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder log = new StringBuilder(); 9758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < childCount; i++) { 9768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ViewHolder vh = getChildViewHolderInt(getChildAt(i)); 977668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar TestViewHolder tvh = (TestViewHolder) vh; 978668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar log.append(tvh.mBindedItem).append(vh) 979668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar .append(" hidden:") 980668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar .append(mChildHelper.mHiddenViews.contains(vh.itemView)) 981668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar .append("\n"); 982668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 983668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (int i = 0; i < childCount; i++) { 984668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar ViewHolder vh = getChildViewHolderInt(getChildAt(i)); 985668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (vh.isInvalid()) { 986668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar continue; 987668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 988668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar if (vh.getPosition() < 0) { 989668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar LayoutManager lm = getLayoutManager(); 990668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (int j = 0; j < lm.getChildCount(); j ++) { 991668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar assertNotSame("removed view holder should not be in LM's child list", 992668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar vh.itemView, lm.getChildAt(j)); 9938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 994668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } else if (!mChildHelper.mHiddenViews.contains(vh.itemView)) { 9958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!existingOffsets.add(vh.getPosition())) { 996668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar throw new IllegalStateException("view holder position conflict for " 997668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar + "existing views " + vh + "\n" + log); 9988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 9998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar void validatePostUpdateOp() { 10048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 10058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validateViewHolderPositions(); 10068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (super.mState.isPreLayout()) { 10078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validatePreLayoutSequence((AnimationLayoutManager) getLayoutManager()); 10088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar validateAdapterPosition((AnimationLayoutManager) getLayoutManager()); 10108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable t) { 10118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar postExceptionToInstrumentation(t); 10128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void validateAdapterPosition(AnimationLayoutManager lm) { 10188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (ViewHolder vh : collectViewHolders()) { 10198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!vh.isRemoved() && vh.mPreLayoutPosition >= 0) { 10208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("adapter position calculations should match view holder " 1021668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar + "pre layout:" + mState.isPreLayout() 1022668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar + " positions\n" + vh + "\n" + lm.getLog(), 10238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper.findPositionOffset(vh.mPreLayoutPosition), vh.mPosition); 10248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // ensures pre layout positions are continuous block. This is not necessarily a case 10298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // but valid in test RV 10308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void validatePreLayoutSequence(AnimationLayoutManager lm) { 10318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Set<Integer> preLayoutPositions = new HashSet<Integer>(); 10328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (ViewHolder vh : collectViewHolders()) { 10338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertTrue("pre layout positions should be distinct " + lm.getLog(), 10348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar preLayoutPositions.add(vh.mPreLayoutPosition)); 10358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int minPos = Integer.MAX_VALUE; 10378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (Integer pos : preLayoutPositions) { 10388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (pos < minPos) { 10398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar minPos = pos; 10408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 1; i < preLayoutPositions.size(); i++) { 10438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull("next position should exist " + lm.getLog(), 10448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar preLayoutPositions.contains(minPos + i)); 10458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 1048888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar @Override 1049888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar protected void dispatchDraw(Canvas canvas) { 1050888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar super.dispatchDraw(canvas); 1051888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar if (drawLatch != null) { 1052888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar drawLatch.countDown(); 1053888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 1054888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar } 1055888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar 1056b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 1057b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar void dispatchLayout() { 1058b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar try { 1059b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar super.dispatchLayout(); 1060b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (getLayoutManager() instanceof AnimationLayoutManager) { 1061b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar ((AnimationLayoutManager) getLayoutManager()).onPostDispatchLayout(); 1062b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1063b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } catch (Throwable t) { 1064b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar postExceptionToInstrumentation(t); 1065b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1066b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 1067b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1068b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 10698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar abstract class AdapterOps { 10738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final public void run(TestAdapter adapter) throws Throwable { 10758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar onRun(adapter); 10768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar abstract void onRun(TestAdapter testAdapter) throws Throwable; 10798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static class CollectPositionResult { 10828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar // true if found in scrap 10848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public RecyclerView.ViewHolder scrapResult; 10858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public RecyclerView.ViewHolder adapterResult; 10878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static CollectPositionResult fromScrap(RecyclerView.ViewHolder viewHolder) { 10898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar CollectPositionResult cpr = new CollectPositionResult(); 10908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar cpr.scrapResult = viewHolder; 10918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return cpr; 10928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static CollectPositionResult fromAdapter(RecyclerView.ViewHolder viewHolder) { 10958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar CollectPositionResult cpr = new CollectPositionResult(); 10968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar cpr.adapterResult = viewHolder; 10978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return cpr; 10988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar static class PositionConstraint { 11028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static enum Type { 11048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar scrap, 11058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapter, 11068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar adapterScrap /*first pass adapter, second pass scrap*/ 11078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Type mType; 11108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mOldPos; // if VH 11128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mPreLayoutPos; 11148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mPostLayoutPos; 11168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int mValidateCount = 0; 11188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static PositionConstraint scrap(int oldPos, int preLayoutPos, int postLayoutPos) { 11208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint constraint = new PositionConstraint(); 11218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mType = Type.scrap; 11228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mOldPos = oldPos; 11238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPreLayoutPos = preLayoutPos; 11248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPostLayoutPos = postLayoutPos; 11258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return constraint; 11268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static PositionConstraint adapterScrap(int preLayoutPos, int position) { 11298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint constraint = new PositionConstraint(); 11308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mType = Type.adapterScrap; 11318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mOldPos = RecyclerView.NO_POSITION; 11328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPreLayoutPos = preLayoutPos; 11338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPostLayoutPos = position;// adapter pos does not change 11348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return constraint; 11358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public static PositionConstraint adapter(int position) { 11388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar PositionConstraint constraint = new PositionConstraint(); 11398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mType = Type.adapter; 11408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPreLayoutPos = RecyclerView.NO_POSITION; 11418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mOldPos = RecyclerView.NO_POSITION; 11428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar constraint.mPostLayoutPos = position;// adapter pos does not change 11438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return constraint; 11448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void assertValidate() { 11478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar int expectedValidate = 0; 11488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mPreLayoutPos >= 0) { 11498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar expectedValidate ++; 11508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mPostLayoutPos >= 0) { 11528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar expectedValidate ++; 11538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals("should run all validates", expectedValidate, mValidateCount); 11558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 11588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 11598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Cons{" + 11608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "t=" + mType.name() + 11618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", old=" + mOldPos + 11628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", pre=" + mPreLayoutPos + 11638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar ", post=" + mPostLayoutPos + 11648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 11658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void validate(RecyclerView.State state, CollectPositionResult result, String log) { 11688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mValidateCount ++; 11698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull(this + ": result should not be null\n" + log, result); 11708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder viewHolder; 11718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mType == Type.scrap || (mType == Type.adapterScrap && !state.isPreLayout())) { 11728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull(this + ": result should come from scrap\n" + log, result.scrapResult); 11738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder = result.scrapResult; 11748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 11758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertNotNull(this + ": result should come from adapter\n" + log, 11768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar result.adapterResult); 11778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": old position should be none when it came from adapter\n" + log, 11788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.NO_POSITION, result.adapterResult.getOldPosition()); 11798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder = result.adapterResult; 11808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (state.isPreLayout()) { 11828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": pre-layout position should match\n" + log, mPreLayoutPos, 1183e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar viewHolder.mPreLayoutPosition == -1 ? viewHolder.mPosition : 11848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder.mPreLayoutPosition); 11858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": pre-layout getPosition should match\n" + log, mPreLayoutPos, 11868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar viewHolder.getPosition()); 11878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mType == Type.scrap) { 11888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertEquals(this + ": old position should match\n" + log, mOldPos, 11898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar result.scrapResult.getOldPosition()); 1190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 11918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else if (mType == Type.adapter || mType == Type.adapterScrap || !result.scrapResult 11928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar .isRemoved()) { 1193e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertEquals(this + ": post-layout position should match\n" + log + "\n\n" 1194e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar + viewHolder, mPostLayoutPos, viewHolder.getPosition()); 1195b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1196b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1197b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 1198d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase} 1199