10a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 30a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * 40a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 50a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * you may not use this file except in compliance with the License. 60a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * You may obtain a copy of the License at 70a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * 80a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 90a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * 100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * Unless required by applicable law or agreed to in writing, software 110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * See the License for the specific language governing permissions and 140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * limitations under the License. 150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar */ 160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget; 180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.LayoutState.LAYOUT_START; 20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL; 21ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.StaggeredGridLayoutManager.HORIZONTAL; 22754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikas 23754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.hamcrest.CoreMatchers.hasItem; 24754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.hamcrest.CoreMatchers.is; 25754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.hamcrest.CoreMatchers.not; 26754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.hamcrest.CoreMatchers.sameInstance; 27754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertEquals; 28754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertNotNull; 29754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertThat; 30754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertTrue; 310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.graphics.Rect; 330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.os.Looper; 340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.os.Parcel; 350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.os.Parcelable; 36b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viveretteimport android.support.test.filters.FlakyTest; 3735232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikasimport android.support.test.filters.LargeTest; 38b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viveretteimport android.support.test.filters.Suppress; 390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.util.Log; 400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.View; 414510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport android.view.ViewParent; 420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 43c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.NonNull; 44c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas 45754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.junit.Test; 46754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.junit.runner.RunWith; 47754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.junit.runners.Parameterized; 48754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikas 490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Arrays; 500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.BitSet; 510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.List; 520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Map; 530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.UUID; 540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(Parameterized.class) 5635232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas@LargeTest 570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpublic class StaggeredGridLayoutManagerBaseConfigSetTest 580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar extends BaseStaggeredGridLayoutManagerTest { 590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Parameterized.Parameters(name = "{0}") 610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public static List<Config> getParams() { 620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return createBaseVariations(); 630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar private final Config mConfig; 660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public StaggeredGridLayoutManagerBaseConfigSetTest(Config config) 680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws CloneNotSupportedException { 690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig = (Config) config.clone(); 700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void rTL() throws Throwable { 744143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar rtlTest(false, false); 750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void rTLChangeAfter() throws Throwable { 794143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar rtlTest(true, false); 800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 824143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar @Test 834143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public void rTLItemWrapContent() throws Throwable { 844143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar rtlTest(false, true); 854143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 864143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 874143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar @Test 884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public void rTLChangeAfterItemWrapContent() throws Throwable { 894143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar rtlTest(true, true); 904143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 924143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar void rtlTest(boolean changeRtlAfter, final boolean wrapContent) throws Throwable { 930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mSpanCount == 1) { 940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig.mSpanCount = 2; 950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String logPrefix = mConfig + ", changeRtlAfterLayout:" + changeRtlAfter; 974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar setupByConfig(mConfig.itemCount(5), 984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar new GridTestAdapter(mConfig.mItemCount, mConfig.mOrientation) { 994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar @Override 1008a11e6829c522aa1efcc903afa4c01d337082eabChris Craik public void onBindViewHolder(@NonNull TestViewHolder holder, 1014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int position) { 1024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar super.onBindViewHolder(holder, position); 1034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (wrapContent) { 1044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == HORIZONTAL) { 1054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar holder.itemView.getLayoutParams().height 1064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar = RecyclerView.LayoutParams.WRAP_CONTENT; 1074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 1084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar holder.itemView.getLayoutParams().width 1094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar = RecyclerView.LayoutParams.MATCH_PARENT; 1104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 1114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 1124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 1134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar }); 1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (changeRtlAfter) { 1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 1160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 1170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.setFakeRtl(true); 1180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 1190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 1200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.mFakeRTL = true; 1210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals("view should become rtl", true, mLayoutManager.isLayoutRTL()); 1250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OrientationHelper helper = OrientationHelper.createHorizontalHelper(mLayoutManager); 1260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child0 = mLayoutManager.findViewByPosition(0); 1270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child1 = mLayoutManager.findViewByPosition(mConfig.mOrientation == VERTICAL ? 1 1280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : mConfig.mSpanCount); 1290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(logPrefix + " child position 0 should be laid out", child0); 1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(logPrefix + " child position 0 should be laid out", child1); 1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar logPrefix += " child1 pos:" + mLayoutManager.getPosition(child1); 1320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mOrientation == VERTICAL || !mConfig.mReverseLayout) { 1330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(logPrefix + " second child should be to the left of first child", 1340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar helper.getDecoratedEnd(child0) > helper.getDecoratedEnd(child1)); 1350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(logPrefix + " first child should be right aligned", 1360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar helper.getDecoratedEnd(child0), helper.getEndAfterPadding()); 1370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(logPrefix + " first child should be to the left of second child", 1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar helper.getDecoratedStart(child1) >= helper.getDecoratedStart(child0)); 1400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(logPrefix + " first child should be left aligned", 1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar helper.getDecoratedStart(child0), helper.getStartAfterPadding()); 1420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 1440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollBackAndPreservePositions() throws Throwable { 1480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollBackAndPreservePositionsTest(false); 1490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 1520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollBackAndPreservePositionsWithRestore() throws Throwable { 1530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollBackAndPreservePositionsTest(true); 1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollBackAndPreservePositionsTest(final boolean saveRestoreInBetween) 1570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws Throwable { 1580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(mConfig); 1590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mAdapter.mOnBindCallback = new OnBindCallback() { 1600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 1610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void onBoundItem(TestViewHolder vh, int position) { 1620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams 1630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp = (StaggeredGridLayoutManager.LayoutParams) vh.itemView 1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.setFullSpan((position * 7) % (mConfig.mSpanCount + 1) == 0); 1660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }; 1680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 1690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int[] globalPositions = new int[mAdapter.getItemCount()]; 1700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Arrays.fill(globalPositions, Integer.MIN_VALUE); 1710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int scrollStep = (mLayoutManager.mPrimaryOrientation.getTotalSpace() / 10) 1720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * (mConfig.mReverseLayout ? -1 : 1); 1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int[] globalPos = new int[1]; 17542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 1770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 1780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int globalScrollPosition = 0; 1790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (globalPositions[mAdapter.getItemCount() - 1] == Integer.MIN_VALUE) { 1800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < mRecyclerView.getChildCount(); i++) { 1810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = mRecyclerView.getChildAt(i); 1820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int pos = mRecyclerView.getChildLayoutPosition(child); 1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (globalPositions[pos] != Integer.MIN_VALUE) { 1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 1850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mReverseLayout) { 1870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalPositions[pos] = globalScrollPosition + 1880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.mPrimaryOrientation.getDecoratedEnd(child); 1890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 1900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalPositions[pos] = globalScrollPosition + 1910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.mPrimaryOrientation.getDecoratedStart(child); 1920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalScrollPosition += mLayoutManager.scrollBy(scrollStep, 1950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.mRecycler, mRecyclerView.mState); 1960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 1980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "done recording positions " + Arrays.toString(globalPositions)); 1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalPos[0] = globalScrollPosition; 2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 2030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 2040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (saveRestoreInBetween) { 2060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar saveRestore(mConfig); 2070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 21042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 2110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 2120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 2130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int globalScrollPosition = globalPos[0]; 2140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // now scroll back and make sure global positions match 2150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar BitSet shouldTest = new BitSet(mAdapter.getItemCount()); 2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar shouldTest.set(0, mAdapter.getItemCount() - 1, true); 2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String assertPrefix = mConfig + ", restored in between:" + saveRestoreInBetween 2180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " global pos must match when scrolling in reverse for position "; 2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int scrollAmount = Integer.MAX_VALUE; 2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (!shouldTest.isEmpty() && scrollAmount != 0) { 2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < mRecyclerView.getChildCount(); i++) { 2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = mRecyclerView.getChildAt(i); 2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int pos = mRecyclerView.getChildLayoutPosition(child); 2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!shouldTest.get(pos)) { 2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 2260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar shouldTest.clear(pos); 2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int globalPos; 2290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mReverseLayout) { 2300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalPos = globalScrollPosition + 2310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.mPrimaryOrientation.getDecoratedEnd(child); 2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalPos = globalScrollPosition + 2340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.mPrimaryOrientation.getDecoratedStart(child); 2350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(assertPrefix + pos, 2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalPositions[pos], globalPos); 2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollAmount = mLayoutManager.scrollBy(-scrollStep, 2400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.mRecycler, mRecyclerView.mState); 2410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar globalScrollPosition += scrollAmount; 2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue("all views should be seen", shouldTest.isEmpty()); 2440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 2460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar private void saveRestore(final Config config) throws Throwable { 25042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 2510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 2520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 2530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 2540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 2550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // we append a suffix to the parcelable to test out of bounds 2560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 2570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Parcel parcel = Parcel.obtain(); 2580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar savedState.writeToParcel(parcel, 0); 2590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar parcel.writeString(parcelSuffix); 2600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar removeRecyclerView(); 2610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // reset for reading 2620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar parcel.setDataPosition(0); 2630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // re-create 2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 2660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager = new WrappedLayoutManager(config.mSpanCount, 2670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar config.mOrientation); 2680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.setGapStrategy(config.mGapStrategy); 2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar restored.setLayoutManager(mLayoutManager); 2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // use the same adapter for Rect matching 2710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar restored.setAdapter(mAdapter); 2720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar restored.onRestoreInstanceState(savedState); 2730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 2740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 2750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setRecyclerView(restored); 2760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 2770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 2780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setRecyclerView(restored); 2790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 2800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 2820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 2830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 2860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 2870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void getFirstLastChildrenTest() throws Throwable { 2910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar getFirstLastChildrenTest(false); 2920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void getFirstLastChildrenTestProvideArray() throws Throwable { 2960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar getFirstLastChildrenTest(true); 2970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void getFirstLastChildrenTest(final boolean provideArr) throws Throwable { 3000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(mConfig); 3010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 3020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 3030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 3050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 3060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 3070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar VisibleChildren queryResult = new VisibleChildren(mLayoutManager.getSpanCount()); 3080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar queryResult.findFirstPartialVisibleClosestToStart = mLayoutManager 309e5874e666791a58d21eed482fb90e917445da873Chris Craik .findFirstVisibleItemClosestToStart(false); 3100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar queryResult.findFirstPartialVisibleClosestToEnd = mLayoutManager 311e5874e666791a58d21eed482fb90e917445da873Chris Craik .findFirstVisibleItemClosestToEnd(false); 3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar queryResult.firstFullyVisiblePositions = mLayoutManager 3130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .findFirstCompletelyVisibleItemPositions( 3140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar provideArr ? new int[mLayoutManager.getSpanCount()] : null); 3150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar queryResult.firstVisiblePositions = mLayoutManager 3160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .findFirstVisibleItemPositions( 3170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar provideArr ? new int[mLayoutManager.getSpanCount()] : null); 3180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar queryResult.lastFullyVisiblePositions = mLayoutManager 3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .findLastCompletelyVisibleItemPositions( 3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar provideArr ? new int[mLayoutManager.getSpanCount()] : null); 3210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar queryResult.lastVisiblePositions = mLayoutManager 3220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .findLastVisibleItemPositions( 3230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar provideArr ? new int[mLayoutManager.getSpanCount()] : null); 3240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(mConfig + ":\nfirst visible child should match traversal result\n" 3250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "traversed:" + visibleChildren + "\n" 3260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "queried:" + queryResult + "\n" 3270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + boundsLog, visibleChildren, queryResult 3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }; 33142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(viewInBoundsTest); 3320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 3330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // case 3340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int scrollPosition = mAdapter.getItemCount(); 33542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 3360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 3380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 3390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 3410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 3420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 34342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(viewInBoundsTest); 3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 3450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Thread.sleep(400); 3460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // delete all items 3480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(2); 3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mAdapter.deleteAndNotify(0, mAdapter.getItemCount()); 3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 3510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // test empty case 35242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(viewInBoundsTest); 3530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // set a new adapter with huge items to test full bounds check 3540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 3550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int totalSpace = mLayoutManager.mPrimaryOrientation.getTotalSpace(); 3560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 3570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3588a11e6829c522aa1efcc903afa4c01d337082eabChris Craik public void onBindViewHolder(@NonNull TestViewHolder holder, 3590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int position) { 3600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.onBindViewHolder(holder, position); 3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mOrientation == LinearLayoutManager.HORIZONTAL) { 3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 100); 3630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 3640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 100); 3650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }; 36842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 3690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 3710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setAdapter(newAdapter); 3720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 3740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 37542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(viewInBoundsTest); 3760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 3770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 3790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // case 38042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 3830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int diff; 3840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mReverseLayout) { 3850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar diff = -1; 3860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 3870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar diff = 1; 3880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int distance = diff * 10; 3900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mOrientation == HORIZONTAL) { 3910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.scrollBy(distance, 0); 3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.scrollBy(0, distance); 3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 39742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(viewInBoundsTest); 3980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar checkForMainThreadException(); 3990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 4020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void viewSnapTest() throws Throwable { 4030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Config config = ((Config) mConfig.clone()).itemCount(mConfig.mSpanCount + 1); 4040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(config); 4050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mAdapter.mOnBindCallback = new OnBindCallback() { 4060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void onBoundItem(TestViewHolder vh, int position) { 4080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams 4090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp = (StaggeredGridLayoutManager.LayoutParams) vh.itemView 4100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 4110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (config.mOrientation == HORIZONTAL) { 4120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.width = mRecyclerView.getWidth() / 3; 4130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 4140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.height = mRecyclerView.getHeight() / 3; 4150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean assignRandomSize() { 4200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 4210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }; 4230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 4240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // run these tests twice. once initial layout, once after scroll 4250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String logSuffix = ""; 4260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < 2; i++) { 4270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> itemRectMap = mLayoutManager.collectChildCoordinates(); 4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect recyclerViewBounds = getDecoratedRecyclerViewBounds(); 4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // workaround for SGLM's span distribution issue. Right now, it may leave gaps so we 4300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // avoid it by setting its layout params directly 4310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (config.mOrientation == HORIZONTAL) { 4320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar recyclerViewBounds.bottom -= recyclerViewBounds.height() % config.mSpanCount; 4330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 4340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar recyclerViewBounds.right -= recyclerViewBounds.width() % config.mSpanCount; 4350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect usedLayoutBounds = new Rect(); 4380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Rect rect : itemRectMap.values()) { 4390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.union(rect); 4400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "testing view snapping (" + logSuffix + ") for config " + config); 4440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (config.mOrientation == VERTICAL) { 4460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap on left" + logSuffix, 4470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.left, recyclerViewBounds.left); 4480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap on right" + logSuffix, 4490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.right, recyclerViewBounds.right); 4500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (config.mReverseLayout) { 4510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap on bottom" + logSuffix, 4520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.bottom, recyclerViewBounds.bottom); 4530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(config + " there should be some gap on top" + logSuffix, 4540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.top > recyclerViewBounds.top); 4550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 4560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap on top" + logSuffix, 4570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.top, recyclerViewBounds.top); 4580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(config + " there should be some gap at the bottom" + logSuffix, 4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.bottom < recyclerViewBounds.bottom); 4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap on top" + logSuffix, 4630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.top, recyclerViewBounds.top); 4640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap at the bottom" + logSuffix, 4650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.bottom, recyclerViewBounds.bottom); 4660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (config.mReverseLayout) { 4670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no on right" + logSuffix, 4680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.right, recyclerViewBounds.right); 4690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(config + " there should be some gap on left" + logSuffix, 4700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.left > recyclerViewBounds.left); 4710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 4720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(config + " there should be no gap on left" + logSuffix, 4730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.left, recyclerViewBounds.left); 4740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(config + " there should be some gap on right" + logSuffix, 4750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar usedLayoutBounds.right < recyclerViewBounds.right); 4760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int scroll = config.mReverseLayout ? -500 : 500; 4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollBy(scroll); 4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar logSuffix = " scrolled " + scroll; 4810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 4850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollToPositionWithOffsetTest() throws Throwable { 4860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(mConfig); 4870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 4880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 4890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .createOrientationHelper(mLayoutManager, mConfig.mOrientation); 4900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect layoutBounds = getDecoratedRecyclerViewBounds(); 4910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // try scrolling towards head, should not affect anything 4920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 4930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollToPositionWithOffset(0, 20); 4940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual(mConfig + " trying to over scroll with offset should be no-op", 4950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before, mLayoutManager.collectChildCoordinates()); 4960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // try offsetting some visible children 4970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int testCount = 10; 4980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (testCount-- > 0) { 4990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // get middle child 5000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2); 5010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int position = mRecyclerView.getChildLayoutPosition(child); 5020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int startOffset = mConfig.mReverseLayout ? 5030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 5040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getDecoratedEnd(child) 5050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 5060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getStartAfterPadding(); 5070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int scrollOffset = startOffset / 2; 5080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 5090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollToPositionWithOffset(position, scrollOffset); 5100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 5110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int finalOffset = mConfig.mReverseLayout ? 5120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 5130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getDecoratedEnd(child) 5140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 5150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getStartAfterPadding(); 5160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(mConfig + " scroll with offset on a visible child should work fine", 5170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollOffset, finalOffset); 5180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // try scrolling to invisible children 5210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar testCount = 10; 5220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // we test above and below, one by one 5230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int offsetMultiplier = -1; 5240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (testCount-- > 0) { 5250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final TargetTuple target = findInvisibleTarget(mConfig); 5260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 5270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int offset = offsetMultiplier 5280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3; 5290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollToPositionWithOffset(target.mPosition, offset); 5300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 5310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final View child = mLayoutManager.findViewByPosition(target.mPosition); 5320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(mConfig + " scrolling to a mPosition with offset " + offset 5330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " should layout it", child); 5340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Rect bounds = mLayoutManager.getViewBounds(child); 5350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 5360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, mConfig + " post scroll to invisible mPosition " + bounds + " in " 5370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + layoutBounds + " with offset " + offset); 5380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mReverseLayout) { 5410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(mConfig + " when scrolling with offset to an invisible in reverse " 5420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "layout, its end should align with recycler view's end - offset", 5430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getEndAfterPadding() - offset, 5440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedEnd(child) 5450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 5460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 5470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(mConfig + " when scrolling with offset to an invisible child in normal" 5480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " layout its start should align with recycler view's start + " 5490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "offset", 5500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getStartAfterPadding() + offset, 5510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedStart(child) 5520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 5530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar offsetMultiplier *= -1; 5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 5590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollToPositionTest() throws Throwable { 5600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(mConfig); 5610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 5620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 5630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .createOrientationHelper(mLayoutManager, mConfig.mOrientation); 5640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect layoutBounds = getDecoratedRecyclerViewBounds(); 5650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 5660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View view = mLayoutManager.getChildAt(i); 5670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect bounds = mLayoutManager.getViewBounds(view); 5680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (layoutBounds.contains(bounds)) { 5690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> initialBounds = mLayoutManager.collectChildCoordinates(); 5700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int position = mRecyclerView.getChildLayoutPosition(view); 5710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams layoutParams 5720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar = (StaggeredGridLayoutManager.LayoutParams) (view.getLayoutParams()); 5730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar TestViewHolder vh = (TestViewHolder) layoutParams.mViewHolder; 5740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals("recycler view mPosition should match adapter mPosition", position, 5750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar vh.mBoundItem.mAdapterIndex); 5760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 5770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "testing scroll to visible mPosition at " + position 5780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " " + bounds + " inside " + layoutBounds); 5790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 5810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollToPosition(position); 5820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 5830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 5840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar view = mLayoutManager.findViewByPosition(position); 5850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect newBounds = mLayoutManager.getViewBounds(view); 5860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "after scrolling to visible mPosition " + 5870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar bounds + " equals " + newBounds); 5880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual( 5910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig + "scroll to mPosition on fully visible child should be no-op", 5920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar initialBounds, mLayoutManager.collectChildCoordinates()); 5930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 5940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int position = mRecyclerView.getChildLayoutPosition(view); 5950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 5960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, 5970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar "child(" + position + ") not fully visible " + bounds + " not inside " 5980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + layoutBounds 5990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + mRecyclerView.getChildLayoutPosition(view) 6000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 6010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 60342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 6040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 6050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 6060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.scrollToPosition(position); 6070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 6090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 6100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar view = mLayoutManager.findViewByPosition(position); 6110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar bounds = mLayoutManager.getViewBounds(view); 6120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 6130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "after scroll to partially visible child " + bounds + " in " 6140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + layoutBounds); 6150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(mConfig 6170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " after scrolling to a partially visible child, it should become fully " 6180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " visible. " + bounds + " not inside " + layoutBounds, 6190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutBounds.contains(bounds) 6200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 6210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue( 6220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig + " when scrolling to a partially visible item, one of its edges " 6230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "should be on the boundaries", 6240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getStartAfterPadding() == 6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedStart(view) 6260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar || orientationHelper.getEndAfterPadding() == 6270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedEnd(view)); 6280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // try scrolling to invisible children 6320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int testCount = 10; 6330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (testCount-- > 0) { 6340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final TargetTuple target = findInvisibleTarget(mConfig); 6350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 6360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollToPosition(target.mPosition); 6370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 6380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final View child = mLayoutManager.findViewByPosition(target.mPosition); 6390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(mConfig + " scrolling to a mPosition should lay it out", child); 6400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Rect bounds = mLayoutManager.getViewBounds(child); 6410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 6420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, mConfig + " post scroll to invisible mPosition " + bounds + " in " 6430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + layoutBounds); 6440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(mConfig + " scrolling to a mPosition should make it fully visible", 6460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutBounds.contains(bounds)); 6470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (target.mLayoutDirection == LAYOUT_START) { 6480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals( 6490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig + " when scrolling to an invisible child above, its start should" 6500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " align with recycler view's start", 6510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getStartAfterPadding(), 6520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedStart(child) 6530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 6540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 6550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(mConfig + " when scrolling to an invisible child below, its end " 6560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "should align with recycler view's end", 6570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getEndAfterPadding(), 6580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedEnd(child) 6590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 6600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 6650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scollByTest() throws Throwable { 6660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(mConfig); 6670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 6680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // try invalid scroll. should not happen 6690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final View first = mLayoutManager.getChildAt(0); 6700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OrientationHelper primaryOrientation = OrientationHelper 6710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .createOrientationHelper(mLayoutManager, mConfig.mOrientation); 6720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int scrollDist; 6730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mReverseLayout) { 6740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollDist = primaryOrientation.getDecoratedMeasurement(first) / 2; 6750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 6760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollDist = -primaryOrientation.getDecoratedMeasurement(first) / 2; 6770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 6790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollBy(scrollDist); 6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> after = mLayoutManager.collectChildCoordinates(); 6810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual( 6820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig + " if there are no more items, scroll should not happen (dt:" + scrollDist 6830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + ")", 6840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before, after 6850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollDist = -scrollDist * 3; 6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before = mLayoutManager.collectChildCoordinates(); 6890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollBy(scrollDist); 6900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar after = mLayoutManager.collectChildCoordinates(); 6910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int layoutStart = primaryOrientation.getStartAfterPadding(); 6920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int layoutEnd = primaryOrientation.getEndAfterPadding(); 6930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect afterRect = after.get(entry.getKey()); 6950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // offset rect 6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mOrientation == VERTICAL) { 6970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar entry.getValue().offset(0, -scrollDist); 6980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 6990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar entry.getValue().offset(-scrollDist, 0); 7000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (afterRect == null || afterRect.isEmpty()) { 7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // assert item is out of bounds 7030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int start, end; 7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mConfig.mOrientation == VERTICAL) { 7050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar start = entry.getValue().top; 7060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar end = entry.getValue().bottom; 7070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 7080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar start = entry.getValue().left; 7090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar end = entry.getValue().right; 7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue( 7120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mConfig + " if item is missing after relayout, it should be out of bounds." 7130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + "item start: " + start + ", end:" + end + " layout start:" 7140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + layoutStart + 7150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", layout end:" + layoutEnd, 7160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar start <= layoutStart && end <= layoutEnd || 7170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar start >= layoutEnd && end >= layoutEnd 7180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 7190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 7200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(mConfig + " Item should be laid out at the scroll offset coordinates", 7210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar entry.getValue(), 7220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 7230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertViewPositions(mConfig); 7260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 7290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void layoutOrderTest() throws Throwable { 7300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(mConfig); 7310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertViewPositions(mConfig); 7320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 7350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void consistentRelayout() throws Throwable { 7360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar consistentRelayoutTest(mConfig, false); 7370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 7400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void consistentRelayoutWithFullSpanFirstChild() throws Throwable { 7410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar consistentRelayoutTest(mConfig, true); 7420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 744b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viverette @Suppress 745b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viverette @FlakyTest(bugId = 34158822) 7464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Test 747b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viverette @LargeTest 7484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void dontRecycleViewsTranslatedOutOfBoundsFromStart() throws Throwable { 7494510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final Config config = ((Config) mConfig.clone()).itemCount(1000); 7504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar setupByConfig(config); 7514510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar waitFirstLayout(); 7524510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar // pick position from child count so that it is not too far away 7534510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar int pos = mRecyclerView.getChildCount() * 2; 7544510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar smoothScrollToPosition(pos, true); 7554510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(pos); 7564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar OrientationHelper helper = mLayoutManager.mPrimaryOrientation; 7574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar int gap = helper.getDecoratedStart(vh.itemView); 7584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(gap); 7594510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar gap = helper.getDecoratedStart(vh.itemView); 7604510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat("test sanity", gap, is(0)); 7614510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 7624510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final int size = helper.getDecoratedMeasurement(vh.itemView); 7634510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView); 76442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 7654510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Override 7664510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void run() { 7674510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mConfig.mOrientation == HORIZONTAL) { 768ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas vh.itemView.setTranslationX(size * 2); 7694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } else { 770ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas vh.itemView.setTranslationY(size * 2); 7714510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 7724510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 7734510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar }); 7744510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(size * 2); 7754510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), not(hasItem(sameInstance(vh.itemView)))); 7764510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.itemView.getParent(), is((ViewParent) mRecyclerView)); 7774510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.getAdapterPosition(), is(pos)); 7784510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(size * 2); 7794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), hasItem(sameInstance(vh.itemView))); 7804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 7814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 7824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Test 7834510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void dontRecycleViewsTranslatedOutOfBoundsFromEnd() throws Throwable { 7844510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final Config config = ((Config) mConfig.clone()).itemCount(1000); 7854510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar setupByConfig(config); 7864510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar waitFirstLayout(); 7874510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar // pick position from child count so that it is not too far away 7884510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar int pos = mRecyclerView.getChildCount() * 2; 7894510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar mLayoutManager.expectLayouts(1); 7904510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollToPosition(pos); 7914510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar mLayoutManager.waitForLayout(2); 7924510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(pos); 7934510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar OrientationHelper helper = mLayoutManager.mPrimaryOrientation; 7944510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar int gap = helper.getEnd() - helper.getDecoratedEnd(vh.itemView); 7954510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(-gap); 7964510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar gap = helper.getEnd() - helper.getDecoratedEnd(vh.itemView); 7974510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat("test sanity", gap, is(0)); 7984510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 7994510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final int size = helper.getDecoratedMeasurement(vh.itemView); 8004510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView); 80142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 8024510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Override 8034510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void run() { 8044510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mConfig.mOrientation == HORIZONTAL) { 805ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas vh.itemView.setTranslationX(-size * 2); 8064510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } else { 807ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas vh.itemView.setTranslationY(-size * 2); 8084510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 8094510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 8104510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar }); 8114510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(-size * 2); 8124510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), not(hasItem(sameInstance(vh.itemView)))); 8134510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.itemView.getParent(), is((ViewParent) mRecyclerView)); 8144510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.getAdapterPosition(), is(pos)); 8154510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(-size * 2); 8164510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), hasItem(sameInstance(vh.itemView))); 8174510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 8184510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 8190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void consistentRelayoutTest(Config config, boolean firstChildMultiSpan) 8200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws Throwable { 8210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(config); 8220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (firstChildMultiSpan) { 8230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mAdapter.mFullSpanItems.add(0); 8240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitFirstLayout(); 8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // record all child positions 8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 8280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar requestLayoutOnUIThread(mRecyclerView); 8290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> after = mLayoutManager.collectChildCoordinates(); 8300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual( 8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar config + " simple re-layout, firstChildMultiSpan:" + firstChildMultiSpan, before, 8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar after); 8330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // scroll some to create inconsistency 8340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View firstChild = mLayoutManager.getChildAt(0); 8350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int firstChildStartBeforeScroll = mLayoutManager.mPrimaryOrientation 8360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getDecoratedStart(firstChild); 8370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int distance = mLayoutManager.mPrimaryOrientation.getDecoratedMeasurement(firstChild) / 2; 8380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (config.mReverseLayout) { 8390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar distance *= -1; 8400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar scrollBy(distance); 8420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitForMainThread(2); 8430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue("scroll by should move children", firstChildStartBeforeScroll != 8440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.mPrimaryOrientation.getDecoratedStart(firstChild)); 8450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before = mLayoutManager.collectChildCoordinates(); 8460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 8470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar requestLayoutOnUIThread(mRecyclerView); 8480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 8490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar after = mLayoutManager.collectChildCoordinates(); 8500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual(config + " simple re-layout after scroll", before, after); 8510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar} 853