LinearLayoutManagerTest.java revision 1f5c7b76bfc3da85513e6d2c6aa1058339f0c625
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; 23a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat; 24a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityEventCompat; 25a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 26542f1260934df280985294eaef1ec8469863281fYigit Boyarimport android.util.AttributeSet; 278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log; 28d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View; 29504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 30a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent; 31310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 32d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 33c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END; 34c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START; 35310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 36310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 38d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 40d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 43d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 44d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 45504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 46d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 69310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 70d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 71d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 72d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 73d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 74d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 75d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 76d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 77d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 94542f1260934df280985294eaef1ec8469863281fYigit Boyar mRecyclerView = new WrappedRecyclerView(getActivity()); 95542f1260934df280985294eaef1ec8469863281fYigit Boyar 968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 97504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 98504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar : config.mTestAdapter; 998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 102d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 10349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 1048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 1058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 1068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 1078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItem() throws Throwable { 1111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout( 1131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar false), 100, 0); 1141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemReverse() throws Throwable { 1171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(true), 100, 1191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 0); 1201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEnd() throws Throwable { 1231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(false), 100, 1251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEndAndReverse() throws Throwable { 1291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(true), 100, 1311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemHorizontal() throws Throwable { 1351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout( 1371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar false), 100, 0); 1381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemReverseHorizontal() throws Throwable { 1411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(true), 1431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 100, 0); 1441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEndHorizontal() throws Throwable { 1471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(false), 1491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 100, 99); 1501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEndAndReverseHorizontal() throws Throwable { 1531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(true), 100, 1551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar /** 1591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * This tests a regression where predictive animations were not working as expected when the 1601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * first item is removed and there aren't any more items to add from that direction. 1611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * First item refers to the default anchor item. 1621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar */ 1631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void removeAnchorItemTest(final Config config, int adapterSize, 1641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int removePos) throws Throwable { 1651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar config.adapter(new TestAdapter(adapterSize) { 1661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar @Override 1671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 1681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar int position) { 1691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar super.onBindViewHolder(holder, position); 1701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); 1711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (!(lp instanceof ViewGroup.MarginLayoutParams)) { 1721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar lp = new ViewGroup.MarginLayoutParams(0, 0); 1731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar holder.itemView.setLayoutParams(lp); 1741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp; 1761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int maxSize; 1771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar maxSize = mRecyclerView.getWidth(); 1791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.height = ViewGroup.MarginLayoutParams.FILL_PARENT; 1801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar maxSize = mRecyclerView.getHeight(); 1821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.width = ViewGroup.MarginLayoutParams.FILL_PARENT; 1831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int desiredSize; 1861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (position == removePos) { 1871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // make it large 1881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar desiredSize = maxSize / 4; 1891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // make it small 1911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar desiredSize = maxSize / 8; 1921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.width = desiredSize; 1951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.height = desiredSize; 1971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar }); 2001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar setupByConfig(config, true); 2011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 2021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder toBeRemoved = null; 2031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>(); 2041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = 0; i < childCount; i++) { 2051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View child = mLayoutManager.getChildAt(i); 2061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); 2071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (holder.getAdapterPosition() == removePos) { 2081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar toBeRemoved = holder; 2091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 2101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar toBeMoved.add(holder); 2111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertNotNull("test sanity", toBeRemoved); 2141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertEquals("test sanity", childCount - 1, toBeMoved.size()); 2151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator(); 2161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mRecyclerView.setItemAnimator(loggingItemAnimator); 2171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.reset(); 2181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.expectRunPendingAnimationsCall(1); 2191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutManager.expectLayouts(2); 2201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mTestAdapter.deleteAndNotify(removePos, 1); 2211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutManager.waitForLayout(1); 2221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.waitForPendingAnimationsCall(2); 2231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("removed child should receive remove animation", 2241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mRemoveVHs.contains(toBeRemoved)); 2251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (RecyclerView.ViewHolder vh : toBeMoved) { 2261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("view holder should be in moved list", 2271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mMoveVHs.contains(vh)); 2281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>(); 2301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 2311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View child = mLayoutManager.getChildAt(i); 2321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); 2331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (toBeRemoved != holder && !toBeMoved.contains(holder)) { 2341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar newHolders.add(holder); 2351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("some new children should show up for the new space", newHolders.size() > 0); 2381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertEquals("no items should receive animate add since they are not new", 0, 2391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mAddVHs.size()); 2401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (RecyclerView.ViewHolder holder : newHolders) { 2411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("new holder should receive a move animation", 2421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mMoveVHs.contains(holder)); 2431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("control against adding too many children due to bad layout state preparation." 2451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(), 2461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/); 2471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 249310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void testKeepFocusOnRelayout() throws Throwable { 250310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true); 251310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int center = (mLayoutManager.findLastVisibleItemPosition() 252310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar - mLayoutManager.findFirstVisibleItemPosition()) / 2; 253115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(center); 254310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView); 255310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 256310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 257310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 258310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar vh.itemView.requestFocus(); 259310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 260310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 261310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertTrue("view should have the focus", vh.itemView.hasFocus()); 262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 263310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(2); 264310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int childCountToAdd = mRecyclerView.getChildCount() * 2; 265310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mTestAdapter.addAndNotify(center, childCountToAdd); 266310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar center += childCountToAdd; // offset item 267310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(2); 268310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 269115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForLayoutPosition(center); 270310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertNotNull("focused child should stay in layout", postVH); 271310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 272310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("focused child's screen position should stay unchanged", top, 273310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView)); 274310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 275310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 276542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepFullFocusOnResize() throws Throwable { 277542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true); 278542f1260934df280985294eaef1ec8469863281fYigit Boyar } 279542f1260934df280985294eaef1ec8469863281fYigit Boyar 280542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepPartialFocusOnResize() throws Throwable { 281542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false); 282542f1260934df280985294eaef1ec8469863281fYigit Boyar } 283542f1260934df280985294eaef1ec8469863281fYigit Boyar 284542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepReverseFullFocusOnResize() throws Throwable { 285542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true); 286542f1260934df280985294eaef1ec8469863281fYigit Boyar } 287542f1260934df280985294eaef1ec8469863281fYigit Boyar 288542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepReversePartialFocusOnResize() throws Throwable { 289542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false); 290542f1260934df280985294eaef1ec8469863281fYigit Boyar } 291542f1260934df280985294eaef1ec8469863281fYigit Boyar 292542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepStackFromEndFullFocusOnResize() throws Throwable { 293542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true); 294542f1260934df280985294eaef1ec8469863281fYigit Boyar } 295542f1260934df280985294eaef1ec8469863281fYigit Boyar 296542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepStackFromEndPartialFocusOnResize() throws Throwable { 297542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false); 298542f1260934df280985294eaef1ec8469863281fYigit Boyar } 299542f1260934df280985294eaef1ec8469863281fYigit Boyar 300542f1260934df280985294eaef1ec8469863281fYigit Boyar public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable { 301542f1260934df280985294eaef1ec8469863281fYigit Boyar setupByConfig(config, true); 302542f1260934df280985294eaef1ec8469863281fYigit Boyar final int targetPosition; 303542f1260934df280985294eaef1ec8469863281fYigit Boyar if (config.mStackFromEnd) { 304542f1260934df280985294eaef1ec8469863281fYigit Boyar targetPosition = mLayoutManager.findFirstVisibleItemPosition(); 305542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 306542f1260934df280985294eaef1ec8469863281fYigit Boyar targetPosition = mLayoutManager.findLastVisibleItemPosition(); 307542f1260934df280985294eaef1ec8469863281fYigit Boyar } 308542f1260934df280985294eaef1ec8469863281fYigit Boyar final OrientationHelper helper = mLayoutManager.mOrientationHelper; 309542f1260934df280985294eaef1ec8469863281fYigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView 310542f1260934df280985294eaef1ec8469863281fYigit Boyar .findViewHolderForLayoutPosition(targetPosition); 311542f1260934df280985294eaef1ec8469863281fYigit Boyar 312542f1260934df280985294eaef1ec8469863281fYigit Boyar // scroll enough to offset the child 313542f1260934df280985294eaef1ec8469863281fYigit Boyar int startMargin = helper.getDecoratedStart(vh.itemView) - 314542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 315542f1260934df280985294eaef1ec8469863281fYigit Boyar int endMargin = helper.getEndAfterPadding() - 316542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 317542f1260934df280985294eaef1ec8469863281fYigit Boyar Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin); 318542f1260934df280985294eaef1ec8469863281fYigit Boyar requestFocus(vh.itemView); 319542f1260934df280985294eaef1ec8469863281fYigit Boyar runTestOnUiThread(new Runnable() { 320542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 321542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 322542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("view should gain the focus", vh.itemView.hasFocus()); 323542f1260934df280985294eaef1ec8469863281fYigit Boyar } 324542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 325542f1260934df280985294eaef1ec8469863281fYigit Boyar do { 326542f1260934df280985294eaef1ec8469863281fYigit Boyar Thread.sleep(100); 327542f1260934df280985294eaef1ec8469863281fYigit Boyar } while (mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE); 328542f1260934df280985294eaef1ec8469863281fYigit Boyar // scroll enough to offset the child 329542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(vh.itemView) - 330542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 331542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - 332542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 333542f1260934df280985294eaef1ec8469863281fYigit Boyar 334542f1260934df280985294eaef1ec8469863281fYigit Boyar Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin); 335542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0); 336542f1260934df280985294eaef1ec8469863281fYigit Boyar 337542f1260934df280985294eaef1ec8469863281fYigit Boyar int expectedOffset = 0; 338542f1260934df280985294eaef1ec8469863281fYigit Boyar boolean offsetAtStart = false; 339542f1260934df280985294eaef1ec8469863281fYigit Boyar if (!fullyVisible) { 340542f1260934df280985294eaef1ec8469863281fYigit Boyar // move it a bit such that it is no more fully visible 341542f1260934df280985294eaef1ec8469863281fYigit Boyar final int childSize = helper 342542f1260934df280985294eaef1ec8469863281fYigit Boyar .getDecoratedMeasurement(vh.itemView); 343542f1260934df280985294eaef1ec8469863281fYigit Boyar expectedOffset = childSize / 3; 344542f1260934df280985294eaef1ec8469863281fYigit Boyar if (startMargin < endMargin) { 345542f1260934df280985294eaef1ec8469863281fYigit Boyar scrollBy(expectedOffset); 346542f1260934df280985294eaef1ec8469863281fYigit Boyar offsetAtStart = true; 347542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 348542f1260934df280985294eaef1ec8469863281fYigit Boyar scrollBy(-expectedOffset); 349542f1260934df280985294eaef1ec8469863281fYigit Boyar offsetAtStart = false; 350542f1260934df280985294eaef1ec8469863281fYigit Boyar } 351542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(vh.itemView) - 352542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 353542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - 354542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 355542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("test sanity, view should not be fully visible", startMargin < 0 356542f1260934df280985294eaef1ec8469863281fYigit Boyar || endMargin < 0); 357542f1260934df280985294eaef1ec8469863281fYigit Boyar } 358542f1260934df280985294eaef1ec8469863281fYigit Boyar 359542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.expectLayouts(1); 360542f1260934df280985294eaef1ec8469863281fYigit Boyar runTestOnUiThread(new Runnable() { 361542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 362542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 363542f1260934df280985294eaef1ec8469863281fYigit Boyar final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams(); 364542f1260934df280985294eaef1ec8469863281fYigit Boyar if (config.mOrientation == HORIZONTAL) { 365542f1260934df280985294eaef1ec8469863281fYigit Boyar layoutParams.width = mRecyclerView.getWidth() / 2; 366542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 367542f1260934df280985294eaef1ec8469863281fYigit Boyar layoutParams.height = mRecyclerView.getHeight() / 2; 368542f1260934df280985294eaef1ec8469863281fYigit Boyar } 369542f1260934df280985294eaef1ec8469863281fYigit Boyar mRecyclerView.setLayoutParams(layoutParams); 370542f1260934df280985294eaef1ec8469863281fYigit Boyar } 371542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 372542f1260934df280985294eaef1ec8469863281fYigit Boyar Thread.sleep(100); 373542f1260934df280985294eaef1ec8469863281fYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 374542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.waitForLayout(2); 375542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 376542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("view should preserve the focus", vh.itemView.hasFocus()); 377542f1260934df280985294eaef1ec8469863281fYigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView 378542f1260934df280985294eaef1ec8469863281fYigit Boyar .findViewHolderForLayoutPosition(targetPosition); 379542f1260934df280985294eaef1ec8469863281fYigit Boyar assertNotNull("focused child should stay in layout", postVH); 380542f1260934df280985294eaef1ec8469863281fYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 381542f1260934df280985294eaef1ec8469863281fYigit Boyar View focused = postVH.itemView; 382542f1260934df280985294eaef1ec8469863281fYigit Boyar 383542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding(); 384542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused); 385542f1260934df280985294eaef1ec8469863281fYigit Boyar 386542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child should be somewhat visible", 387542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedStart(focused) < helper.getEndAfterPadding() 388542f1260934df280985294eaef1ec8469863281fYigit Boyar && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding()); 389542f1260934df280985294eaef1ec8469863281fYigit Boyar if (fullyVisible) { 390542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child end should stay fully visible", 391542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin >= 0); 392542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child start should stay fully visible", 393542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin >= 0); 394542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 395542f1260934df280985294eaef1ec8469863281fYigit Boyar if (offsetAtStart) { 396542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("start should preserve its offset", startMargin < 0); 397542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("end should be visible", endMargin >= 0); 398542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 399542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("end should preserve its offset", endMargin < 0); 400542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("start should be visible", startMargin >= 0); 401542f1260934df280985294eaef1ec8469863281fYigit Boyar } 402542f1260934df280985294eaef1ec8469863281fYigit Boyar } 403542f1260934df280985294eaef1ec8469863281fYigit Boyar } 404542f1260934df280985294eaef1ec8469863281fYigit Boyar 405245b9720dad47a694d16a1d0f48ad462bc27989fYigit Boyar public void testResize() throws Throwable { 406310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5 407310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar , Config.DEFAULT_ITEM_COUNT)) { 408310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar stackFromEndTest(config); 409310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar removeRecyclerView(); 410310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 411310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 412310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 413c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public void testScrollToPositionWithOffset() throws Throwable { 414c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar for (Config config : mBaseVariations) { 415c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffsetTest(config.itemCount(300)); 416c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar removeRecyclerView(); 417c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 418c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 419c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 420c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public void scrollToPositionWithOffsetTest(Config config) throws Throwable { 421c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar setupByConfig(config, true); 422c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 423c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .createOrientationHelper(mLayoutManager, config.mOrientation); 424c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Rect layoutBounds = getDecoratedRecyclerViewBounds(); 425c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // try scrolling towards head, should not affect anything 426c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 427c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (config.mStackFromEnd) { 428c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1, 429c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.mOrientationHelper.getEnd() - 500); 430c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 431c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(0, 20); 432c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 433c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertRectSetsEqual(config + " trying to over scroll with offset should be no-op", 434c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar before, mLayoutManager.collectChildCoordinates()); 435c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // try offsetting some visible children 436c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar int testCount = 10; 437c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar while (testCount-- > 0) { 438c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // get middle child 439c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2); 440115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final int position = mRecyclerView.getChildLayoutPosition(child); 441c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int startOffset = config.mReverseLayout ? 442c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 443c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getDecoratedEnd(child) 444c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 445c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getStartAfterPadding(); 446c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2 447c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar : startOffset / 2; 448c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.expectLayouts(1); 449c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(position, scrollOffset); 450c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.waitForLayout(2); 451c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int finalOffset = config.mReverseLayout ? 452c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 453c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getDecoratedEnd(child) 454c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 455c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getStartAfterPadding(); 456c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertEquals(config + " scroll with offset on a visible child should work fine " + 457c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar " offset:" + finalOffset + " , existing offset:" + startOffset + ", " 458c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + "child " + position, 459c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollOffset, finalOffset); 460c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 461c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 462c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // try scrolling to invisible children 463c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar testCount = 10; 464c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // we test above and below, one by one 465c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar int offsetMultiplier = -1; 466c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar while (testCount-- > 0) { 467c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final TargetTuple target = findInvisibleTarget(config); 468c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final String logPrefix = config + " " + target; 469c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.expectLayouts(1); 470c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int offset = offsetMultiplier 471c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3; 472c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(target.mPosition, offset); 473c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.waitForLayout(2); 474c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final View child = mLayoutManager.findViewByPosition(target.mPosition); 475c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset 476c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + " should layout it", child); 477c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final Rect bounds = mLayoutManager.getViewBounds(child); 478c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (DEBUG) { 479c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in " 480c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + layoutBounds + " with offset " + offset); 481c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 482c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 483c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (config.mReverseLayout) { 484c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse " 485c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + "layout, its end should align with recycler view's end - offset", 486c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getEndAfterPadding() - offset, 487c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getDecoratedEnd(child) 488c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 489c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 490c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertEquals(logPrefix + " when scrolling with offset to an invisible child in normal" 491c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + " layout its start should align with recycler view's start + " 492c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + "offset", 493c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getStartAfterPadding() + offset, 494c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getDecoratedStart(child) 495c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 496c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 497c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar offsetMultiplier *= -1; 498c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 499c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 500c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 501c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar private TargetTuple findInvisibleTarget(Config config) { 502c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE; 503c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 504c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar View child = mLayoutManager.getChildAt(i); 505115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar int position = mRecyclerView.getChildLayoutPosition(child); 506c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (position < minPosition) { 507c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar minPosition = position; 508c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 509c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (position > maxPosition) { 510c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar maxPosition = position; 511c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 512c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 513c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int tailTarget = maxPosition + 514c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2; 515c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int headTarget = minPosition / 2; 516c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int target; 517c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // where will the child come from ? 518c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int itemLayoutDirection; 519c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) { 520c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar target = tailTarget; 521c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END; 522c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 523c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar target = headTarget; 524c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START; 525c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 526c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (DEBUG) { 527c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Log.d(TAG, 528c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition); 529c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 530c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new TargetTuple(target, itemLayoutDirection); 531c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 532c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 533310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void stackFromEndTest(final Config config) throws Throwable { 534310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final FrameLayout container = getRecyclerViewContainer(); 535310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 536310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 537310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 538310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, 0, 0); 539310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 540310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 541310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 542310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(config, true); 543310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition(); 544310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition(); 545310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition(); 546310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition(); 547310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(1); 548310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // resize the recycler view to half 549310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 550310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 551310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 552310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mOrientation == HORIZONTAL) { 553310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, container.getWidth() / 2, 0); 554310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 555310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, 0, container.getWidth() / 2); 556310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 557310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 558310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 559310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(1); 560310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mStackFromEnd) { 561310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last visible position should not change.", 562310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition()); 563310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last completely visible position should not change", 564310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar lastCompletelyVisibleItemPosition, 565310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition()); 566310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 567310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: first visible position should not change.", 568310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition()); 569310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last completely visible position should not change", 570310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar firstCompletelyVisibleItemPosition, 571310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition()); 572310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 573310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 574310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 5756e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void testScrollToPositionWithPredictive() throws Throwable { 5766e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET); 5776e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 5786e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(3, 20); 5796e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 5806e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 5816e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar LinearLayoutManager.INVALID_OFFSET); 5826e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 5836e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10); 5846e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 5856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 5866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset) 5876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throws Throwable { 588310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 5896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 5906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.mOnLayoutListener = new OnLayoutListener() { 5916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 5926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 5936e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (state.isPreLayout()) { 5946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position should still be pending", 5956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollPosition, mLayoutManager.mPendingScrollPosition); 5966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 5976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position offset should still be pending", 5986e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset, mLayoutManager.mPendingScrollPositionOffset); 5996e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6006e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 6016e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar RecyclerView.ViewHolder vh = 602115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar mRecyclerView.findViewHolderForLayoutPosition(scrollPosition); 6036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertNotNull("scroll to position should work", vh); 6046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 6056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("scroll offset should be applied properly", 6066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getPaddingTop() + scrollOffset + 6076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar ((RecyclerView.LayoutParams) vh.itemView 6086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar .getLayoutParams()).topMargin, 6096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getDecoratedTop(vh.itemView)); 6106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }; 6146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.expectLayouts(2); 6156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar runTestOnUiThread(new Runnable() { 6166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 6176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void run() { 6186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 6196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mTestAdapter.addAndNotify(0, 1); 6206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) { 6216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPosition(scrollPosition); 6226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 6236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPositionWithOffset(scrollPosition, 6246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset); 6256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 6276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 6286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throwable.printStackTrace(); 6296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 6316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }); 6336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.waitForLayout(2); 6346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 6356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 6378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 6398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 642d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 643504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 644504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 645504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(300) { 646504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 647504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 648504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 649504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 650504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType); 651504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 652504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder); 653504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 654504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return testViewHolder; 655504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 656504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 657310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(300) 658504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .adapter(adapter), true); 659504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 660504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 661504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 662504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 663504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 664504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int cnt = childCount.decrementAndGet(); 665504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 666504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap); 667504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 668504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 669504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 670504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 671504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 672504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 673504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 674504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 675504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 676504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView); 677504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 678504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 679504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 680504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 681504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 682504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500); 683504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 684504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 685504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 686504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 687504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 688504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 689504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(15); 690504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 691504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 692504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 693504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 694504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 695504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 696504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now trigger lots of add again, followed by a scroll to position 697504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 698504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(5 + (i % 3) * 3, 1); 699504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(25); 700504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 701504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20); 702504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 703504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 704504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 705504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 706504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 707504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 708d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 709d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 710d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 711d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 712d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 713d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 714d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 71549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testDontRecycleChildrenOnDetach() throws Throwable { 71649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 71749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 71849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 71949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 72049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 72149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 72249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 72349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 72449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 72549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 72649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 72749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 72849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testRecycleChildrenOnDetach() throws Throwable { 72949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 73049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 73149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 73249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 73349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 73449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 73549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 73649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 73749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 73849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 73949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 74049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 74149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 74249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 74349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 744d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 7458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 746d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 752d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 753d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 754d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 755d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 756d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 762d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 780d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 781d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 784d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 7858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 793d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 794d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 795d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 796d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 797d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 798d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 799d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 800d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 801310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mOrientation == HORIZONTAL) { 802d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 803d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 804d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 805d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 806d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 807d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 808d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 810d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 811d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 813d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 817d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 8188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 8198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 8208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 8248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 8298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 8358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 8368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 8378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 8428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 8488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 8498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 8508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 8518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 8568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 8628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 8638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar -50); 8648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 8658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 8708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 8738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 8758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 8768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 8798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 8828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 8848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 8858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 8868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 8878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 8918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 8928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 8978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 9008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 9028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 9038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 9048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 9148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 9168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 9178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 9198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 9208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 9218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 9258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 9268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 9308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 9318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 93275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 93375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 93449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 93549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar void onAfterRestore(Config config) throws Throwable { 93649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach; 93749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 93849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 93949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 94049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 94149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean shouldLayoutMatch(Config config) { 94249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return true; 94349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 94449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 94549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 94649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar String describe() { 947b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return "Change should recycle children"; 94849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 94949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }, 95049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar new PostRestoreRunnable() { 95175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 95275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 95375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 95475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 95575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 95675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 95775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 95875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 95975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 96075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 96175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 96275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 96375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 96475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 965aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 96675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 96775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 96875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 96975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 97075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 971aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 97275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 973aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 974aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 975aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 97675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 97775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 9788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 98075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 98149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300); 98249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true); 98349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar for (Config config : variations) { 9848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 9858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 9868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 9878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable, 9888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreRunnable); 9898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 9908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void savedStateTest(Config config, boolean waitForLayout, 9988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 9998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 10008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 10018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 10028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 10038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 10048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 10068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 10078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 10088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 10098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 10118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 10128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 10138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 10148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 10158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 10168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 10178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 10188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 10198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 10208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 10218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 10228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 10238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 10248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 10258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 10268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 1027b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar config.mOrientation, config.mReverseLayout); 1028b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 10298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 10308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 10318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 10328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 10338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 10348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 10358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 10368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 10378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 10388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 10398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 10408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 10418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 10428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 10438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 10448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 10458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 10468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 10478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 10488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 10498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 10508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 10518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 10528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 10538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 10548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 10558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 105649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + ": on restore with changes, previous view positions should NOT " 105749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + "be preserved", 10588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar before, mLayoutManager.collectChildCoordinates()); 10598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 106075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 10618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 10648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 10658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 10668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 10678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 10688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 10698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 10718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 10738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 10748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> after) { 10758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 10768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 10778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual("NOT " + message, before, after); 10788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 10798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 10808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 10828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 10848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 1085668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder sb = new StringBuilder(); 1086668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("checking rectangle equality."); 1087668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("before:\n"); 1088668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 1089668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 1090668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 1091668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("after:\n"); 1092668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 1093668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 10948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1095668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar message = message + "\n" + sb.toString(); 10968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 10978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 10988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 10998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect afterRect = after.get(entry.getKey()); 11008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + ":\nSame item should be visible after simple re-layout", 11018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar afterRect); 11028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 11038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 11048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1107a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void testAccessibilityPositions() throws Throwable { 1108a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 1109a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityDelegateCompat delegateCompat = mRecyclerView 1110a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar .getCompatAccessibilityDelegate(); 1111a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityEvent event = AccessibilityEvent.obtain(); 1112a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar runTestOnUiThread(new Runnable() { 1113a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 1114a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void run() { 1115a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event); 1116a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1117a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar }); 1118a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityRecordCompat record = AccessibilityEventCompat 1119a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar .asRecord(event); 1120a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar assertEquals("result should have first position", 1121a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar record.getFromIndex(), 1122a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mLayoutManager.findFirstVisibleItemPosition()); 1123a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar assertEquals("result should have last position", 1124a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar record.getToIndex(), 1125a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mLayoutManager.findLastVisibleItemPosition()); 1126a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1127a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 1128d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 1129d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1130d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 1131d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1132d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 1133d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1134d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 1135d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1136d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 1137d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1138d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1139d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 1140d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 1141d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 1142d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 1143d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 1144d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 1145d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 1146d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1147d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1148d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 11498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 11508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 11528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 11548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 11578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 11598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 11628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 11648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 11658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 116675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 116775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 116875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 116975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 11708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 1173d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1174d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 1175d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 11768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 11778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11786e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar OnLayoutListener mOnLayoutListener; 11796e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1180d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 1181d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 1182d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1183d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1184d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 1185d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 1186d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1187d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1188d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 1189d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 1190d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1191d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 11928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 11938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 11948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 11958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 11968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1199504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 1200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 1201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 1202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleView(child, recycler); 1204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 1206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 1207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 1208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 1209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 1210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleViewAt(index, recycler); 1212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 1214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 121594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 121694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 12178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 121894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 121994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 12208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 12218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 12228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1223d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 12246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit); 1225d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 1226d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 12276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar getInstrumentation().waitForIdleSync(); 1228d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1229d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1230d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 1231d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 1232d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 1233d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 1234d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 1235d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 1236d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 1237d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 1238d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 1239d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 1240d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 1241d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 1242d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1243d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 1244d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1245d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1246310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException { 1247310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); 1248310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (itemAnimator == null) { 1249310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return; 1250310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1251310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final CountDownLatch latch = new CountDownLatch(1); 1252310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final boolean running = itemAnimator.isRunning( 1253310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 1254310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 1255310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void onAnimationsFinished() { 1256310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar latch.countDown(); 1257310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1258310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1259310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar ); 1260310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (running) { 1261310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar latch.await(timeoutInSeconds, TimeUnit.SECONDS); 1262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1263310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1264310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 1265d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 1266d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 1267d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 1268d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 1269d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 1270d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 1271d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 1272d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 1273d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 1274d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 1275d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 1276d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 1277d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 1278d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1279d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 1280d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 1281d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 1282d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 1283d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 1284d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1285d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1286d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 1287d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 1288d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1289d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1290d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1291d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 1292d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 1293d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 1294d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1295d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1296d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 1297d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 1298d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1299d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1300d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1301d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 1302d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1303d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 13048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 13058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 13068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 13078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 13088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 13098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 13108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 13118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 13128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 13138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 13148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 13158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 13168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 13178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 13228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 13238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 13248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 13258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 13268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 13278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 13288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 13298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 13308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 13318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 1332115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar items.put(vh.mBoundItem, getViewBounds(child)); 13338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 13368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 13378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1339d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1340d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 13416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 13426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 13436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.before(recycler, state); 13446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 13456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar super.onLayoutChildren(recycler, state); 13466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 13476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.after(recycler, state); 13486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 13496e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable t) { 13506e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar postExceptionToInstrumentation(t); 13516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1352d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 1353d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 13546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 13556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 13566e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 13576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 13586e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar static class OnLayoutListener { 13596e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state){} 13606e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state){} 1361d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1362d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 13638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 1364d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 13658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 1366d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1367d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 1368d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1369310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int mOrientation = VERTICAL; 1370d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1371d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 1372d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 137349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean mRecycleChildrenOnDetach = false; 137449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 1375d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 1376d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar TestAdapter mTestAdapter; 1378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 1379d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 1380d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 1381d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 1382d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 1383d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1384d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1385d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 1386d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1387d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1388d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1389504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Config adapter(TestAdapter adapter) { 1390504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = adapter; 1391504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return this; 1392504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1393504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 139449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 139549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 139649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return this; 139749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 139849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 1399d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 1400d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 1401d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1402d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1403d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1404d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 1405d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 1406d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1407d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1408d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1409d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 1410d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 1411d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1412d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1413d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1414d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 1415d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 1416d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1417d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1418d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 14198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 14208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 14218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 14228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 14238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 14248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1425d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1426d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 1427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 1428d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 1429d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 1430d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 143149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 1432d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 1433d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 1434d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1435d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1436542f1260934df280985294eaef1ec8469863281fYigit Boyar 1437542f1260934df280985294eaef1ec8469863281fYigit Boyar private static class WrappedRecyclerView extends RecyclerView { 1438542f1260934df280985294eaef1ec8469863281fYigit Boyar 1439542f1260934df280985294eaef1ec8469863281fYigit Boyar public WrappedRecyclerView(Context context) { 1440542f1260934df280985294eaef1ec8469863281fYigit Boyar super(context); 1441542f1260934df280985294eaef1ec8469863281fYigit Boyar init(context); 1442542f1260934df280985294eaef1ec8469863281fYigit Boyar } 1443542f1260934df280985294eaef1ec8469863281fYigit Boyar 1444542f1260934df280985294eaef1ec8469863281fYigit Boyar public WrappedRecyclerView(Context context, AttributeSet attrs) { 1445542f1260934df280985294eaef1ec8469863281fYigit Boyar super(context, attrs); 1446542f1260934df280985294eaef1ec8469863281fYigit Boyar init(context); 1447542f1260934df280985294eaef1ec8469863281fYigit Boyar } 1448542f1260934df280985294eaef1ec8469863281fYigit Boyar 1449542f1260934df280985294eaef1ec8469863281fYigit Boyar public WrappedRecyclerView(Context context, AttributeSet attrs, int defStyle) { 1450542f1260934df280985294eaef1ec8469863281fYigit Boyar super(context, attrs, defStyle); 1451542f1260934df280985294eaef1ec8469863281fYigit Boyar init(context); 1452542f1260934df280985294eaef1ec8469863281fYigit Boyar } 1453542f1260934df280985294eaef1ec8469863281fYigit Boyar 1454542f1260934df280985294eaef1ec8469863281fYigit Boyar private void init(Context context) { 1455542f1260934df280985294eaef1ec8469863281fYigit Boyar initializeScrollbars(null); 1456542f1260934df280985294eaef1ec8469863281fYigit Boyar } 1457542f1260934df280985294eaef1ec8469863281fYigit Boyar } 1458d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 1459