LinearLayoutManagerTest.java revision 504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6a
1d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar/* 2d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Copyright (C) 2014 The Android Open Source Project 3d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 4d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * you may not use this file except in compliance with the License. 6d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * You may obtain a copy of the License at 7d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 8d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 10d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * See the License for the specific language governing permissions and 14d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * limitations under the License. 15d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 16d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 17d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpackage android.support.v7.widget; 18d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 19d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.content.Context; 208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.graphics.Rect; 218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcel; 228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcelable; 238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log; 24d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View; 25504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 26d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 27504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.lang.ref.WeakReference; 288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 29d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 34d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 35d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 36504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 45d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 46d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 51d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 52d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int orientation : new int[]{LinearLayoutManager.VERTICAL, 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar LinearLayoutManager.HORIZONTAL}) { 62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView = new RecyclerView(getActivity()); 878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 88504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 89504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar : config.mTestAdapter; 908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 91d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 92d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 93d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 9449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 102d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 1038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 104d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 105d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 106d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 107504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 108504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 109504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(300) { 110504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 111504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 112504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 113504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 114504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType); 115504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 116504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder); 117504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 118504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return testViewHolder; 119504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 120504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 121504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar setupByConfig(new Config(LinearLayoutManager.VERTICAL, false, false).itemCount(300) 122504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .adapter(adapter), true); 123504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 124504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 125504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 126504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 127504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 128504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int cnt = childCount.decrementAndGet(); 129504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 130504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap); 131504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 132504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 133504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 134504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 135504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 136504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 137504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 138504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 139504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 140504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView); 141504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 142504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 143504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 144504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 145504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 146504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500); 147504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 148504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 149504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 150504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 151504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 152504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 153504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(15); 154504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 155504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 156504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 157504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 158504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 159504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 160504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now trigger lots of add again, followed by a scroll to position 161504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 162504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(5 + (i % 3) * 3, 1); 163504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(25); 164504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 165504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20); 166504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 167504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 168504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 169504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 170504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 171504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 172d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 173d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 174d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 175d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 176d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 177d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 178d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 17949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testDontRecycleChildrenOnDetach() throws Throwable { 18049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 18149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 18249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 18349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 18449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 18549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 18649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 18749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 18849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 18949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 19049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 19149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 19249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testRecycleChildrenOnDetach() throws Throwable { 19349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 19449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 19549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 19649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 19749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 19849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 19949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 20049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 20149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 20249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 20349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 20449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 20549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 20649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 20749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 208d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 2098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 210d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 211d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 212d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 213d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 214d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 215d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 216d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 217d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 218d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 219d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 220d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 221d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 222d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 223d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 224d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 225d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 226d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 227d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 228d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 229d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 230d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 231d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 232d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 233d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 234d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 235d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 236d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 237d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 238d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 239d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 240d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 241d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 242d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 243d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 244d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 245d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 246d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 247d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 248d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 2498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 250d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 251d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 252d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 253d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 254d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 255d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 256d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 257d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 258d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 259d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 260d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 261d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 262d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 263d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 264d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 265d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (config.mOrientation == LinearLayoutManager.HORIZONTAL) { 266d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 267d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 268d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 269d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 270d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 271d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 272d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 273d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 274d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 275d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 276d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 277d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 278d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 279d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 280d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 281d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 2828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 28349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Thread.sleep(5000); 2848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 2858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 2898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 2938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 2948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 2968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 2978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 2998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 3018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 3078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 3108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 3128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 3148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 3158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 3218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 3248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 3288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 3298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 3308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 3358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 3388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 3408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 3418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 3478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 3498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 3518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 3528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 3568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 3578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 3628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 3658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 3678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 3688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 3698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 3738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 3748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 3798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 3818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 3828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 3848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 3858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 3868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 3908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 3918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 3958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 3968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 39775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 39875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 39949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 40049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar void onAfterRestore(Config config) throws Throwable { 40149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach; 40249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 40349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 40449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 40549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 40649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean shouldLayoutMatch(Config config) { 40749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return true; 40849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 40949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 41049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 41149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar String describe() { 41249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return "Change shoudl recycle children"; 41349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 41449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }, 41549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar new PostRestoreRunnable() { 41675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 41775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 41875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 41975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 42075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 42175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 42275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 42375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 42475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 42575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 42675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 42775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 42875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 42975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 430aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 43175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 43275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 43375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 43475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 43575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 436aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 43775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 438aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 439aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 440aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 44175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 44275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 44575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 44649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300); 44749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true); 44849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar for (Config config : variations) { 4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 4548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 4558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 4638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 4648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 4658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 4668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 4678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 4688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 4698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 4718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 4728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 4738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 4748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 4768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 4778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 4788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 4798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 4808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 4818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 4828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 4838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 4848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 4858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 4868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 4878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 4888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 4898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 4908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 4918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 4928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1 - config.mOrientation, !config.mReverseLayout); 4938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(!config.mStackFromEnd); 4948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 4958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 4968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 4978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 4988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 4998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 5008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 5018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 5028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 5038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 5048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 5058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 5068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 5078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 5088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 5098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 5108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 5118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 5128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 51349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals(logPrefix + " on saved state, mRecycleChildrenOnDetach should be preserved", 51449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach, mLayoutManager.getRecycleChildrenOnDetach()); 5158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 5168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 5178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 5188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 5198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 5208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 5218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 5228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 52349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + ": on restore with changes, previous view positions should NOT " 52449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + "be preserved", 5258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 52775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 5328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 5338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 5348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 5358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 5368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 5388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 5418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 5428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 5438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 5448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 5458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 5468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 5478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 5498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 552668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder sb = new StringBuilder(); 553668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("checking rectangle equality."); 554668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("before:\n"); 555668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 556668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 557668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 558668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("after:\n"); 559668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 560668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 5618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 562668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar message = message + "\n" + sb.toString(); 5638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 5648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 5658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 5668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 5678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 5688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 5698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 5708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 5718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 574d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 575d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 576d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 577d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 578d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 579d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 580d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 581d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 582d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 583d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 584d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 585d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 586d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 587d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 588d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 589d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 590d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 591d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 592d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 593d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 594d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 5958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 5968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 5988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 6008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 6038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 6058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 6088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 6108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 6118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 61275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 61375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 61475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 61575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 6168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 619d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 620d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 621d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 6238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 624d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 625d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 626d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 627d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 628d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 629d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 630d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 631d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 632d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 633d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 634d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 635d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 6368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 6378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 6388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 6398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 6408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 6428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 643504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 644504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 645504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 646504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 647504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleView(child, recycler); 648504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 649504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 650504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 651504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 652504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 653504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 654504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 655504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleViewAt(index, recycler); 656504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 657504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 658504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 65994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 66094c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 6618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 66294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 66394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 6648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 6668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 667d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 668d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.await(timeout, timeUnit); 669d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 670d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 671d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 672d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 673d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 674d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 675d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 676d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 677d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 678d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 679d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 680d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 681d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 682d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 683d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 684d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 685d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 686d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 687d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 688d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 689d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 690d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 691d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 692d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 693d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 694d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 695d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 696d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 697d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 698d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 699d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 700d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 701d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 702d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 703d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 704d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 705d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 706d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 707d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 708d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 709d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 710d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 711d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 712d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 713d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 714d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 715d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 716d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 717d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 718d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 719d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 720d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 721d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 722d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 723d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 724d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 725d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 726d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 727d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 7298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 7308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 7318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 7328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 7338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 7348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 7358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 7368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 7378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 7388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 7398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 7408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 7418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 7458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 7468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 7478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 7488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 7498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 7508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 7518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 7528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 7538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 7548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 7558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 7568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar items.put(vh.mBindedItem, getViewBounds(child)); 7578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 7608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 7618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 7628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onLayoutChildren(recycler, state); 766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 7728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mOrientation = LinearLayoutManager.VERTICAL; 777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 78049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean mRecycleChildrenOnDetach = false; 78149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 784504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar TestAdapter mTestAdapter; 785504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 793d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 794d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 795d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 796504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Config adapter(TestAdapter adapter) { 797504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = adapter; 798504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return this; 799504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 800504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 80149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 80249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 80349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return this; 80449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 80549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 806d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 807d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 808d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 810d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 811d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 813d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 817d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 818d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 819d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 820d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 821d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 822d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 823d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 824d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 825d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 8298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 8308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 832d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 833d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 834d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 835d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 836d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 837d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 83849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 839d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 840d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 841d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 842d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 843d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 844