LinearLayoutManagerTest.java revision 49c83b12201dde5b93d4eca3d44478e0c967a2e6
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; 25d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 27d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 29d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 32d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 33d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 34d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 42d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 43d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 48d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 49d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 50d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 51d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 52d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int orientation : new int[]{LinearLayoutManager.VERTICAL, 58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar LinearLayoutManager.HORIZONTAL}) { 59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView = new RecyclerView(getActivity()); 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 85d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter = new TestAdapter(config.mItemCount); 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 87d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 88d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 89d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 9049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 98d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 102d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 103d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 104d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 105d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 106d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 107d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 108d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 109d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 11049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testDontRecycleChildrenOnDetach() throws Throwable { 11149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 11249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 11349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 11449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 11549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 11649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 11749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 11849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 11949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 12049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 12149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 12249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 12349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testRecycleChildrenOnDetach() throws Throwable { 12449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 12549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 12649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 12749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 12849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 12949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 13049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 13149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 13249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 13349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 13449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 13549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 13649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 13749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 13849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 139d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 1408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 141d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 142d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 143d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 144d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 145d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 146d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 147d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 148d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 149d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 150d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 151d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 152d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 153d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 154d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 155d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 156d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 157d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 158d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 159d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 160d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 161d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 162d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 163d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 164d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 165d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 166d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 167d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 168d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 169d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 170d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 171d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 172d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 173d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 174d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 175d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 176d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 177d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 178d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 179d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 1808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 181d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 182d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 183d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 184d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 185d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 186d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 187d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 188d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 189d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 190d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 191d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 192d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 193d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 194d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 195d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 196d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (config.mOrientation == LinearLayoutManager.HORIZONTAL) { 197d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 198d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 199d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 200d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 201d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 202d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 203d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 204d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 205d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 206d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 207d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 208d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 209d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 210d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 211d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 212d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 2138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 21449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Thread.sleep(5000); 2158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 2168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 2208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 2258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 2328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 2388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 2458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 2468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 2528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 2598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 2608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 2668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 2698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 2718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 2758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 2818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 2828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 2838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 2888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 2938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 2998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 3008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 3048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 3058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 3108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 3138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 3158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 3168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 3178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 3218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 3228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 32875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 32975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 33049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 33149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar void onAfterRestore(Config config) throws Throwable { 33249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach; 33349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 33449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 33549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 33649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 33749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean shouldLayoutMatch(Config config) { 33849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return true; 33949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 34049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 34149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 34249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar String describe() { 34349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return "Change shoudl recycle children"; 34449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 34549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }, 34649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar new PostRestoreRunnable() { 34775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 34875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 34975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 35075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 35175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 35275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 35375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 35475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 35575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 35675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 35775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 35875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 35975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 36075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 361aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 36275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 36375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 36475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 36575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 36675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 367aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 36875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 369aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 370aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 371aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 37275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 37375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 3748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 37675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 37749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300); 37849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true); 37949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar for (Config config : variations) { 3808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 3818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 3828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 3838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 3848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 3858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 3948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 3958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 3968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 3978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 3988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 3998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 4008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 4028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 4038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 4048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 4058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 4078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 4088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 4098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 4108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 4118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 4128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 4138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 4148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 4158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 4168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 4178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 4188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 4198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 4218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 4228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 4238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1 - config.mOrientation, !config.mReverseLayout); 4248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(!config.mStackFromEnd); 4258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 4268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 4278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 4288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 4298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 4308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 4318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 4328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 4338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 4348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 4358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 4368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 4388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 44449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals(logPrefix + " on saved state, mRecycleChildrenOnDetach should be preserved", 44549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach, mLayoutManager.getRecycleChildrenOnDetach()); 4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 45449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + ": on restore with changes, previous view positions should NOT " 45549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + "be preserved", 4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 45875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 4598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 4638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 4648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 4668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 4678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 4698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 4728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 4738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 4748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 4758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 4768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 4778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 4788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 4808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 483668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder sb = new StringBuilder(); 484668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("checking rectangle equality."); 485668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("before:\n"); 486668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 487668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 488668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 489668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("after:\n"); 490668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 491668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 4928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 493668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar message = message + "\n" + sb.toString(); 4948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 4958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 4968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 4978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 4988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 4998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 5008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 5018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 5028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 505d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 506d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 507d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 508d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 509d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 510d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 511d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 512d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 513d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 514d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 515d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 516d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 517d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 518d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 519d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 520d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 521d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 522d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 523d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 524d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 525d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 5278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 5298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 5318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 5348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 5368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 5398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 5418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 5428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 54375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 54475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 54575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 54675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 5478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 550d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 551d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 552d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 5548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 555d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 556d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 557d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 558d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 559d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 560d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 561d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 562d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 563d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 564d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 565d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 566d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 5698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 5708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 5718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 57494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 57594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 5768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 57794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 57894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 5798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 582d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 583d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.await(timeout, timeUnit); 584d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 585d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 586d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 587d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 588d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 589d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 590d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 591d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 592d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 593d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 594d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 595d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 596d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 597d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 598d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 599d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 600d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 601d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 602d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 603d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 604d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 605d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 606d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 607d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 608d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 609d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 610d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 611d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 612d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 613d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 614d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 615d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 616d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 617d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 618d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 619d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 620d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 621d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 622d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 623d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 624d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 625d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 626d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 627d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 628d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 629d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 630d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 631d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 632d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 633d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 634d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 635d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 636d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 642d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 6448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 6458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 6468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 6478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 6488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 6498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 6508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 6518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 6528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 6538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 6548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 6558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 6568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 6618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 6628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 6638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 6648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 6658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 6668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 6678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 6688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 6698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 6708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 6718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar items.put(vh.mBindedItem, getViewBounds(child)); 6728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 6758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 6768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 678d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 679d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 680d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onLayoutChildren(recycler, state); 681d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 682d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 683d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 684d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 686d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 688d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 689d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 690d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 691d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mOrientation = LinearLayoutManager.VERTICAL; 692d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 693d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 694d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 69549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean mRecycleChildrenOnDetach = false; 69649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 697d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 698d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 699d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 700d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 701d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 702d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 703d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 704d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 705d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 706d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 707d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 708d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 70949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 71049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 71149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return this; 71249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 71349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 714d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 715d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 716d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 717d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 718d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 719d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 720d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 721d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 722d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 723d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 724d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 725d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 726d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 727d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 728d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 729d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 730d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 731d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 732d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 733d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 7358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 7368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 7378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 7388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 740d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 741d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 742d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 743d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 744d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 745d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 74649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 752