LinearLayoutManagerTest.java revision 6e83751247c5be0211d7bffaf057431c03dfef38
1d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar/* 2d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Copyright (C) 2014 The Android Open Source Project 3d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 4d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * you may not use this file except in compliance with the License. 6d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * You may obtain a copy of the License at 7d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 8d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 10d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * See the License for the specific language governing permissions and 14d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * limitations under the License. 15d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 16d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 17d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpackage android.support.v7.widget; 18d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 19d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.content.Context; 208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.graphics.Rect; 218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcel; 228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcelable; 238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log; 24d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View; 25504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 26d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 27504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.lang.ref.WeakReference; 288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 29d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 34d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 35d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 36504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 45d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 46d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 51d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 52d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int orientation : new int[]{LinearLayoutManager.VERTICAL, 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar LinearLayoutManager.HORIZONTAL}) { 62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView = new RecyclerView(getActivity()); 878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 88504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 89504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar : config.mTestAdapter; 908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 91d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 92d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 93d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 9449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1016e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void testScrollToPositionWithPredictive() throws Throwable { 1026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET); 1036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 1046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(3, 20); 1056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 1066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 1076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar LinearLayoutManager.INVALID_OFFSET); 1086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 1096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10); 1106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset) 1136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throws Throwable { 1146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar setupByConfig(new Config(LinearLayoutManager.VERTICAL, false, false), true); 1156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.mOnLayoutListener = new OnLayoutListener() { 1176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 1186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 1196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (state.isPreLayout()) { 1206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position should still be pending", 1216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollPosition, mLayoutManager.mPendingScrollPosition); 1226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 1236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position offset should still be pending", 1246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset, mLayoutManager.mPendingScrollPositionOffset); 1256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 1276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar RecyclerView.ViewHolder vh = 1286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mRecyclerView.findViewHolderForPosition(scrollPosition); 1296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertNotNull("scroll to position should work", vh); 1306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 1316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("scroll offset should be applied properly", 1326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getPaddingTop() + scrollOffset + 1336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar ((RecyclerView.LayoutParams) vh.itemView 1346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar .getLayoutParams()).topMargin, 1356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getDecoratedTop(vh.itemView)); 1366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }; 1406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.expectLayouts(2); 1416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar runTestOnUiThread(new Runnable() { 1426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 1436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void run() { 1446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mTestAdapter.addAndNotify(0, 1); 1466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) { 1476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPosition(scrollPosition); 1486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 1496e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPositionWithOffset(scrollPosition, 1506e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset); 1516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1526e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1536e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 1546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throwable.printStackTrace(); 1556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1566e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1586e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }); 1596e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.waitForLayout(2); 1606e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1616e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1626e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 164d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 1658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 166d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 167d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 168d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 169504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 170504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 171504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(300) { 172504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 173504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 174504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 175504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 176504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType); 177504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 178504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder); 179504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 180504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return testViewHolder; 181504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 182504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 183504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar setupByConfig(new Config(LinearLayoutManager.VERTICAL, false, false).itemCount(300) 184504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .adapter(adapter), true); 185504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 186504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 187504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 188504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 189504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 190504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int cnt = childCount.decrementAndGet(); 191504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 192504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap); 193504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 194504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 195504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 196504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 197504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 198504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 199504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView); 203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500); 209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 215504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(15); 216504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 217504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 218504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 219504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 220504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 221504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 222504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now trigger lots of add again, followed by a scroll to position 223504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 224504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(5 + (i % 3) * 3, 1); 225504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(25); 226504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 227504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20); 228504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 229504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 230504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 231504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 232504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 233504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 234d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 235d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 236d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 237d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 238d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 239d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 240d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 24149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testDontRecycleChildrenOnDetach() throws Throwable { 24249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 24349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 24449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 24549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 24649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 24749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 24849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 24949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 25049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 25149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 25249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 25349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 25449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testRecycleChildrenOnDetach() throws Throwable { 25549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 25649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 25749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 25849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 25949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 26049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 26149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 26249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 26349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 26449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 26549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 26649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 26749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 26849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 26949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 270d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 2718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 272d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 273d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 274d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 275d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 276d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 277d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 278d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 279d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 280d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 281d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 282d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 283d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 284d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 285d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 286d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 287d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 288d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 289d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 290d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 291d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 292d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 293d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 294d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 295d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 296d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 297d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 298d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 299d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 300d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 301d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 302d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 303d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 304d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 305d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 306d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 307d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 308d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 309d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 310d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 3118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 312d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 316d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 317d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 318d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 319d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 320d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 321d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 322d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 323d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 324d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 325d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 326d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 327d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (config.mOrientation == LinearLayoutManager.HORIZONTAL) { 328d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 329d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 330d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 331d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 332d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 333d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 334d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 335d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 336d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 337d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 338d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 339d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 340d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 341d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 342d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 343d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 34549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Thread.sleep(5000); 3468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 3478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 3488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 3518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 3568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 3598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 3618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 3638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 3698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 3728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 3748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 3768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 3778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 3838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 3868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 3888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 3908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 3918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 3978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 4008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 4028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 4038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 4068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 4098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 4118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 4128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 4138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 4148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 4188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 4198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 4248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 4278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 4298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 4308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 4318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 4358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 4368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 4588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 45975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 46075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 46149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 46249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar void onAfterRestore(Config config) throws Throwable { 46349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach; 46449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 46549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 46649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 46749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 46849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean shouldLayoutMatch(Config config) { 46949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return true; 47049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 47149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 47249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 47349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar String describe() { 47449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return "Change shoudl recycle children"; 47549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 47649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }, 47749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar new PostRestoreRunnable() { 47875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 47975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 48075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 48175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 48275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 48375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 48475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 48575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 48675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 48775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 48875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 48975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 49075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 49175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 492aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 49375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 49475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 49575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 49675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 49775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 498aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 49975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 500aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 501aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 502aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 50375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 50475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 5058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 50775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 50849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300); 50949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true); 51049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar for (Config config : variations) { 5118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 5128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 5138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 5148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 5158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 5168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 5178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 5258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 5278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 5298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 5308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 5318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 5338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 5348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 5358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 5368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 5388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 5398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 5408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 5418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 5428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 5438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 5448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 5458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 5468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 5478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 5488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 5498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 5508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 5528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 5538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 5548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1 - config.mOrientation, !config.mReverseLayout); 5558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(!config.mStackFromEnd); 5568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 5578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 5588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 5598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 5608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 5618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 5628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 5638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 5648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 5658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 5668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 5678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 5688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 5698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 5708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 5718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 5728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 5738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 5748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 57549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals(logPrefix + " on saved state, mRecycleChildrenOnDetach should be preserved", 57649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach, mLayoutManager.getRecycleChildrenOnDetach()); 5778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 5788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 5798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 5808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 5818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 5828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 5838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 5848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 58549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + ": on restore with changes, previous view positions should NOT " 58649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + "be preserved", 5878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 5888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 58975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 5908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 5948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 5958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 5978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 5988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 6008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 6038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 6048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 6058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 6068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 6078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 6088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 6098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 6118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 614668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder sb = new StringBuilder(); 615668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("checking rectangle equality."); 616668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("before:\n"); 617668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 618668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 619668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 620668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("after:\n"); 621668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 622668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 6238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 624668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar message = message + "\n" + sb.toString(); 6258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 6268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 6278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 6288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 6298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 6308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 6318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 6328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 6338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 636d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 642d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 643d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 644d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 645d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 646d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 647d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 648d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 649d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 650d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 651d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 652d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 653d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 654d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 655d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 656d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 6588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 6608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 6628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 6658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 6678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 6708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 6728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 6738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 67475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 67575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 67675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 67775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 6788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 681d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 682d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 683d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 6858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar OnLayoutListener mOnLayoutListener; 6876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 688d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 689d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 690d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 691d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 692d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 693d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 694d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 695d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 696d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 697d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 698d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 699d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 7018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 7028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 7038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 7048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 707504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 708504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 709504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 710504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 711504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleView(child, recycler); 712504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 713504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 714504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 715504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 716504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 717504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 718504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 719504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleViewAt(index, recycler); 720504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 721504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 722504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 72394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 72494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 7258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 72694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 72794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 7288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 731d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 7326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit); 733d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 734d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 7356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar getInstrumentation().waitForIdleSync(); 736d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 737d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 738d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 739d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 740d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 741d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 742d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 743d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 744d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 745d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 746d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 752d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 753d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 754d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 755d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 756d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 762d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 780d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 781d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 784d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 785d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 7948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 7958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 7968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 7978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 7988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 7998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 8008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 8018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 8028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 8038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 8048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 8058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 8068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 8118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 8128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 8138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 8158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 8168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 8178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 8188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 8198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 8208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 8218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar items.put(vh.mBindedItem, getViewBounds(child)); 8228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 8258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 828d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 829d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 8306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 8316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 8326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.before(recycler, state); 8336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 8346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar super.onLayoutChildren(recycler, state); 8356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 8366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.after(recycler, state); 8376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 8386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable t) { 8396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar postExceptionToInstrumentation(t); 8406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 841d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 842d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 8436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 8446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 8456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 8466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 8476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar static class OnLayoutListener { 8486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state){} 8496e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state){} 850d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 851d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 8528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 853d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 8548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 855d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 856d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 857d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 858d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mOrientation = LinearLayoutManager.VERTICAL; 859d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 860d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 861d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 86249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean mRecycleChildrenOnDetach = false; 86349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 864d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 865d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 866504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar TestAdapter mTestAdapter; 867504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 868d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 869d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 870d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 871d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 872d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 873d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 874d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 875d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 876d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 877d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 878504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Config adapter(TestAdapter adapter) { 879504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = adapter; 880504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return this; 881504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 882504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 88349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 88449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 88549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return this; 88649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 88749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 888d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 889d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 890d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 891d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 892d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 893d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 894d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 895d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 896d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 897d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 898d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 899d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 900d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 901d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 902d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 903d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 904d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 905d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 906d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 907d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 914d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 915d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 916d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 917d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 918d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 919d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 92049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 921d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 922d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 923d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 924d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 925d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 926