LinearLayoutManagerTest.java revision b8403301bbec29129730f6cce3fe2fa3ee8e1e0b
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; 26310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 27d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 28310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 29310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 33d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 36d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 38504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 39d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 47d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 48d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 62310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 70d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView = new RecyclerView(getActivity()); 888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 89504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 90504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar : config.mTestAdapter; 918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 92d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 93d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 94d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 9549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 102310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void testKeepFocusOnRelayout() throws Throwable { 103310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true); 104310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int center = (mLayoutManager.findLastVisibleItemPosition() 105310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar - mLayoutManager.findFirstVisibleItemPosition()) / 2; 106310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForPosition(center); 107310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView); 108310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 109310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 110310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 111310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar vh.itemView.requestFocus(); 112310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 113310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 114310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertTrue("view should have the focus", vh.itemView.hasFocus()); 115310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 116310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(2); 117310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int childCountToAdd = mRecyclerView.getChildCount() * 2; 118310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mTestAdapter.addAndNotify(center, childCountToAdd); 119310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar center += childCountToAdd; // offset item 120310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(2); 121310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 122310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForPosition(center); 123310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertNotNull("focused child should stay in layout", postVH); 124310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 125310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("focused child's screen position should stay unchanged", top, 126310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView)); 127310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 128310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 129310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void testStackFromEnd() throws Throwable { 130310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5 131310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar , Config.DEFAULT_ITEM_COUNT)) { 132310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar stackFromEndTest(config); 133310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar removeRecyclerView(); 134310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 135310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 136310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 137310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void stackFromEndTest(final Config config) throws Throwable { 138310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final FrameLayout container = getRecyclerViewContainer(); 139310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 140310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 141310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 142310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, 0, 0); 143310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 144310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 145310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 146310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(config, true); 147310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition(); 148310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition(); 149310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition(); 150310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition(); 151310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 152310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(1); 153310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // resize the recycler view to half 154310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 155310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 156310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 157310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mOrientation == HORIZONTAL) { 158310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, container.getWidth() / 2, 0); 159310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 160310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, 0, container.getWidth() / 2); 161310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 162310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 163310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 164310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(1); 165310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mStackFromEnd) { 166310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last visible position should not change.", 167310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition()); 168310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last completely visible position should not change", 169310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar lastCompletelyVisibleItemPosition, 170310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition()); 171310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 172310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: first visible position should not change.", 173310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition()); 174310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last completely visible position should not change", 175310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar firstCompletelyVisibleItemPosition, 176310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition()); 177310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 178310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 179310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 1806e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void testScrollToPositionWithPredictive() throws Throwable { 1816e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET); 1826e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 1836e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(3, 20); 1846e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 1856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 1866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar LinearLayoutManager.INVALID_OFFSET); 1876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 1886e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10); 1896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset) 1926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throws Throwable { 193310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 1946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.mOnLayoutListener = new OnLayoutListener() { 1966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 1976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 1986e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (state.isPreLayout()) { 1996e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position should still be pending", 2006e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollPosition, mLayoutManager.mPendingScrollPosition); 2016e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 2026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position offset should still be pending", 2036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset, mLayoutManager.mPendingScrollPositionOffset); 2046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 2066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar RecyclerView.ViewHolder vh = 2076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mRecyclerView.findViewHolderForPosition(scrollPosition); 2086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertNotNull("scroll to position should work", vh); 2096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 2106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("scroll offset should be applied properly", 2116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getPaddingTop() + scrollOffset + 2126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar ((RecyclerView.LayoutParams) vh.itemView 2136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar .getLayoutParams()).topMargin, 2146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getDecoratedTop(vh.itemView)); 2156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }; 2196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.expectLayouts(2); 2206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar runTestOnUiThread(new Runnable() { 2216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 2226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void run() { 2236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 2246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mTestAdapter.addAndNotify(0, 1); 2256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) { 2266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPosition(scrollPosition); 2276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 2286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPositionWithOffset(scrollPosition, 2296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset); 2306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 2326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 2336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throwable.printStackTrace(); 2346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 2366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }); 2386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.waitForLayout(2); 2396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 2406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 243d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 245d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 246d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 247d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 248504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 249504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 250504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(300) { 251504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 252504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 253504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 254504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 255504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType); 256504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 257504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder); 258504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 259504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return testViewHolder; 260504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 261504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(300) 263504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .adapter(adapter), true); 264504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 265504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 266504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 267504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 268504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 269504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int cnt = childCount.decrementAndGet(); 270504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 271504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap); 272504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 273504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 274504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 275504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 276504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 277504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 278504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 279504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 280504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 281504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView); 282504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 283504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 284504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 285504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 286504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 287504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500); 288504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 289504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 290504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 291504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 292504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 293504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 294504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(15); 295504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 296504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 297504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 298504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 299504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 300504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 301504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now trigger lots of add again, followed by a scroll to position 302504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 303504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(5 + (i % 3) * 3, 1); 304504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(25); 305504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 306504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20); 307504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 308504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 309504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 310504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 311504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 312504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 316d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 317d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 318d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 319d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 32049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testDontRecycleChildrenOnDetach() throws Throwable { 32149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 32249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 32349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 32449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 32549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 32649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 32749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 32849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 32949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 33049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 33149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 33249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 33349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testRecycleChildrenOnDetach() throws Throwable { 33449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 33549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 33649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 33749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 33849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 33949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 34049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 34149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 34249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 34349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 34449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 34549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 34649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 34749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 34849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 349d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 351d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 352d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 353d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 354d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 355d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 356d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 357d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 358d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 359d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 360d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 361d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 362d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 363d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 364d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 365d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 366d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 367d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 368d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 369d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 370d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 371d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 372d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 373d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 374d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 375d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 376d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 377d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 378d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 379d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 380d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 381d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 382d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 383d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 384d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 385d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 386d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 387d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 388d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 389d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 3908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 391d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 392d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 393d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 394d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 395d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 396d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 397d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 398d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 399d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 400d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 401d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 402d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 403d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 404d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 405d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 406310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mOrientation == HORIZONTAL) { 407d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 408d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 409d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 410d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 411d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 412d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 413d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 414d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 415d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 416d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 417d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 418d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 419d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 420d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 421d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 422d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 4238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 4248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 4258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 4268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 4288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 4298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 4348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 4548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 4558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 4618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 4648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 4668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 4678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 4688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 4698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 4708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 4758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 4788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 4808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 4818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 4838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 4848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 4868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 4878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 4898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 4908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 4918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 4928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 4968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 4978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 5018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 5028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 5048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 5058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 5078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 5088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 5098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 5138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 5148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 5188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 5198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 5218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 5228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 5248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 5258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 5308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 5318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 5358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 5368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 53775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 53875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 53949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 54049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar void onAfterRestore(Config config) throws Throwable { 54149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach; 54249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 54349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 54449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 54549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 54649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean shouldLayoutMatch(Config config) { 54749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return true; 54849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 54949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 55049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 55149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar String describe() { 552b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return "Change should recycle children"; 55349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 55449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }, 55549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar new PostRestoreRunnable() { 55675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 55775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 55875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 55975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 56075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 56175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 56275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 56375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 56475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 56575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 56675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 56775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 56875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 56975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 570aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 57175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 57275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 57375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 57475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 57575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 576aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 57775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 578aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 579aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 580aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 58175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 58275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 5838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 58575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 58649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300); 58749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true); 58849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar for (Config config : variations) { 5898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 5908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 5918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 5928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 5938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 5948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 5958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 6038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 6048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 6058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 6068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 6078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 6088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 6098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 6118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 6128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 6138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 6148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 6168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 6178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 6188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 6198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 6208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 6218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 6228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 6238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 6248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 6258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 6268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 6278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 6288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 6308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 6318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 632b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar config.mOrientation, config.mReverseLayout); 633b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 6348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 6358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 6368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 6378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 6388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 6398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 6408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 6418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 6428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 6438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 6448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 6458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 6468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 6478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 6488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 6498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 6508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 6518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 6528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 6538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 6548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 6558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 6568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 6578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 6588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 6598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 6608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 66149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + ": on restore with changes, previous view positions should NOT " 66249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + "be preserved", 6638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 6648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 66575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 6668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 6708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 6718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 6728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 6738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 6748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 6768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 6798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 6808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 6818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 6828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 6838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 6848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 6858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 6878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 690668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder sb = new StringBuilder(); 691668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("checking rectangle equality."); 692668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("before:\n"); 693668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 694668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 695668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 696668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("after:\n"); 697668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 698668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 6998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 700668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar message = message + "\n" + sb.toString(); 7018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 7028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 7038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 7048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 7058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 7068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 7078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 7088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 7098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 712d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 713d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 714d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 715d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 716d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 717d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 718d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 719d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 720d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 721d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 722d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 723d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 724d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 725d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 726d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 727d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 728d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 729d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 730d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 731d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 732d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 7348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 7368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 7388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 7418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 7438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 7468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 7488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 7498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 75075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 75175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 75275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 75375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 7548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 7618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7626e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar OnLayoutListener mOnLayoutListener; 7636e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 7778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 7788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 7798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 7808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 783504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 784504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 785504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 786504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 787504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleView(child, recycler); 788504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 789504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 790504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 791504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 792504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 793504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 794504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 795504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleViewAt(index, recycler); 796504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 797504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 798504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 79994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 80094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 8018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 80294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 80394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 8048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 807d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 8086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit); 809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 810d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 8116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar getInstrumentation().waitForIdleSync(); 812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 813d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 817d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 818d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 819d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 820d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 821d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 822d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 823d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 824d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 825d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 826d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 827d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 828d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 829d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 830310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException { 831310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); 832310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (itemAnimator == null) { 833310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return; 834310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 835310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final CountDownLatch latch = new CountDownLatch(1); 836310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final boolean running = itemAnimator.isRunning( 837310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 838310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 839310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void onAnimationsFinished() { 840310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar latch.countDown(); 841310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 842310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 843310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar ); 844310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (running) { 845310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar latch.await(timeoutInSeconds, TimeUnit.SECONDS); 846310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 847310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 848310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 849d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 850d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 851d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 852d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 853d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 854d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 855d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 856d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 857d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 858d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 859d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 860d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 861d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 862d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 863d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 864d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 865d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 866d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 867d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 868d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 869d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 870d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 871d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 872d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 873d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 874d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 875d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 876d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 877d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 878d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 879d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 880d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 881d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 882d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 883d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 884d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 885d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 886d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 887d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 8888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 8898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 8908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 8918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 8928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 8938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 8948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 8958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 8968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 8978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 8988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 8998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 9008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 9018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 9068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 9078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 9148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 9158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 9168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar items.put(vh.mBindedItem, getViewBounds(child)); 9178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 9208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 9218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 923d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 924d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 9256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 9266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 9276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.before(recycler, state); 9286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 9296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar super.onLayoutChildren(recycler, state); 9306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 9316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.after(recycler, state); 9326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 9336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable t) { 9346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar postExceptionToInstrumentation(t); 9356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 936d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 937d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 9386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 9396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 9406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 9416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 9426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar static class OnLayoutListener { 9436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state){} 9446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state){} 945d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 946d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 9478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 948d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 9498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 950d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 951d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 952d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 953310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int mOrientation = VERTICAL; 954d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 955d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 956d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 95749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean mRecycleChildrenOnDetach = false; 95849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 959d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 960d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 961504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar TestAdapter mTestAdapter; 962504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 963d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 964d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 965d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 966d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 967d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 968d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 969d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 970d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 971d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 972d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 973504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Config adapter(TestAdapter adapter) { 974504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = adapter; 975504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return this; 976504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 977504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 97849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 97949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 98049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return this; 98149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 98249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 983d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 984d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 985d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 986d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 987d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 988d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 989d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 990d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 991d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 992d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 993d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 994d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 995d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 996d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 997d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 998d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 999d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 1000d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1001d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1002d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 10038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 10048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 10058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 10068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 10078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1009d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1010d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 1011d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 1012d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 1013d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 1014d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 101549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 1016d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 1017d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 1018d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1019d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1020d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 1021