LinearLayoutManagerTest.java revision 94c0b086c12e634976fecd47d442bc7a1a6341bb
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); 908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 97d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 99d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 102d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 103d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 104d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 105d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 106d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 107d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 108d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 109d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 1108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 111d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 112d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 113d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 114d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 115d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 116d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 117d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 118d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 119d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 120d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 121d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 122d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 123d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 124d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 125d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 126d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 127d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 128d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 129d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 130d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 131d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 132d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 133d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 134d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 135d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 136d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 137d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 138d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 139d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 140d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 141d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 142d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 143d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 144d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 145d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 146d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 147d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 148d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 149d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 1508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 151d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 152d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 153d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 154d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 155d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 156d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 157d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 158d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 159d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 160d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 161d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 162d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 163d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 164d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 165d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 166d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (config.mOrientation == LinearLayoutManager.HORIZONTAL) { 167d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 168d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 169d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 170d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 171d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 172d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 173d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 174d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 175d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 176d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 177d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 178d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 179d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 180d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 181d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 182d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 1848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 1858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 1868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 1888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 1898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 1938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 1948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 1968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 1978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 1998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 2018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 2078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 2148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 2158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 2218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 2278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 2288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 2298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 2308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 2358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 2388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 2408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 2508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 2518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 2528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 2578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 2628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 2688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 2698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 2748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 2798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 2828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 2848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 2858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 2868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 2908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 2918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 2968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 29775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 29875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 29975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 30075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 30175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 30275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 30375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 30475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 30575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 30675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 30775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 30875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 30975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 31075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 31175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 31275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 313aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 31475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 31575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 31675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 31775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 31875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 319aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 32075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 321aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 322aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 323aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 32475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 32575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 32875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 3298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : addConfigVariation(mBaseVariations, "mItemCount", 0, 300)) { 3308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 3318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 3328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 3338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 3348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 3358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 3458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 3468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 3478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 3488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 3498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 3528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 3538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 3548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 3558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 3578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 3588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 3598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 3608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 3618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 3628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 3638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 3658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 3668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 3678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 3688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 3698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 3718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 3728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 3738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1 - config.mOrientation, !config.mReverseLayout); 3748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(!config.mStackFromEnd); 3758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 3768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 3778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 3788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 3798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 3808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 3818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 3828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 3848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 3868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 3878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 3888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 3898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 3908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 3918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 3928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 3938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 3948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 3958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 3968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 3978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 3988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 3998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 4008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 4018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 4028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar + ": on restore with changes, previous view positions should NOT be preserved", 4038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 4048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 40575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 4068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 4108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 4118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 4138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 4148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 4168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 4198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 4208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 4218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 4228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 4238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 4248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 4258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 4278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 4308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 4318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "checking rectangle equality."); 4328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "before:"); 4338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 4348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, entry.getKey().originalIndex + ":" + entry.getValue()); 4358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "after:"); 4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 4388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, entry.getKey().originalIndex + ":" + entry.getValue()); 4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 452d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 453d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 454d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 455d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 456d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 457d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 458d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 459d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 460d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 461d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 462d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 463d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 464d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 465d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 466d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 467d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 468d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 469d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 470d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 471d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 472d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 4738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 4748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 4768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 4788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 4818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 4838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 4868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 4888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 4898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 49075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 49175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 49275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 49375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 4948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 497d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 498d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 499d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 5018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 502d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 503d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 504d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 505d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 506d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 507d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 508d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 509d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 510d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 511d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 512d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 513d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 5168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 5178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 5188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 52194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 52294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 5238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 52494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 52594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 529d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 530d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.await(timeout, timeUnit); 531d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 532d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 533d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 534d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 535d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 536d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 537d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 538d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 539d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 540d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 541d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 542d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 543d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 544d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 545d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 546d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 547d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 548d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 549d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 550d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 551d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 552d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 553d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 554d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 555d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 556d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 557d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 558d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 559d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 560d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 561d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 562d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 563d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 564d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 565d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 566d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 567d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 568d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 569d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 570d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 571d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 572d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 573d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 574d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 575d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 576d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 577d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 578d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 579d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 580d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 581d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 582d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 583d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 584d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 585d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 586d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 587d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 588d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 589d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 5918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 5928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 5938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 5948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 5968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 5978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 5988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 6008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 6018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 6028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 6038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 6088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 6098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 6108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 6118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 6128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 6138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 6148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 6158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 6168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 6178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 6188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar items.put(vh.mBindedItem, getViewBounds(child)); 6198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 6228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 6238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 625d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 626d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 627d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onLayoutChildren(recycler, state); 628d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 629d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 630d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 631d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 633d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 635d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 636d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mOrientation = LinearLayoutManager.VERTICAL; 639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 642d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 643d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 644d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 645d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 646d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 647d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 648d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 649d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 650d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 651d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 652d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 653d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 654d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 655d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 656d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 657d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 658d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 659d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 660d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 661d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 662d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 663d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 664d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 665d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 666d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 667d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 668d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 669d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 670d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 671d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 672d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 673d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 6758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 6768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 6778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 6788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 680d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 681d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 682d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 683d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 684d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 685d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 686d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 687d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 688d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 689d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 690d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 691