LinearLayoutManagerBaseConfigSetTest.java revision 4510b5c24adad2b94df9b84c6b73f5534ffe9b57
1999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar/* 2999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Copyright (C) 2015 The Android Open Source Project 3999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * 4999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * you may not use this file except in compliance with the License. 6999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * You may obtain a copy of the License at 7999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * 8999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * 10999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * See the License for the specific language governing permissions and 14999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * limitations under the License. 15999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar */ 16999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 17999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpackage android.support.v7.widget; 18999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 19999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 20999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport org.junit.Test; 21999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport org.junit.runner.RunWith; 22999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport org.junit.runners.Parameterized; 23999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 24999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.graphics.Rect; 254510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport android.support.v4.view.ViewCompat; 26999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest; 27999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.util.Log; 28999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport android.view.View; 294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.view.ViewGroup; 304510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport android.view.ViewParent; 31999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport java.util.ArrayList; 33999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.List; 34999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport java.util.Map; 35999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 36999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END; 37999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START; 38999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 404510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 414510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.hasItem; 424510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.is; 434510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.not; 444510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.sameInstance; 454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static org.junit.Assert.assertEquals; 464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static org.junit.Assert.assertNotNull; 474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.junit.Assert.assertThat; 484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 494143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static android.view.ViewGroup.LayoutParams.FILL_PARENT; 51999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 52999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar/** 53999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * Tests that rely on the basic configuration and does not do any additions / removals 54999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar */ 55999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar@RunWith(Parameterized.class) 564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar@MediumTest 57999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpublic class LinearLayoutManagerBaseConfigSetTest extends BaseLinearLayoutManagerTest { 58999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 59999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar private final Config mConfig; 60999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 61999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public LinearLayoutManagerBaseConfigSetTest(Config config) { 62999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mConfig = config; 63999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 64999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 65999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 66999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Parameterized.Parameters(name = "{0}") 67999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public static List<Config> configs() throws CloneNotSupportedException { 684143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar List<Config> result = new ArrayList<>(); 694143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (Config config : createBaseVariations()) { 704143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar result.add(config); 714143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return result; 73999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 74999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 75999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Test 76999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void scrollToPositionWithOffsetTest() throws Throwable { 77999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Config config = ((Config) mConfig.clone()).itemCount(300); 780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(config, true); 79999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 80999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .createOrientationHelper(mLayoutManager, config.mOrientation); 81999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Rect layoutBounds = getDecoratedRecyclerViewBounds(); 82999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // try scrolling towards head, should not affect anything 83999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 84999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (config.mStackFromEnd) { 85999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1, 86999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.mOrientationHelper.getEnd() - 500); 87999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 88999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollToPositionWithOffset(0, 20); 89999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 90999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertRectSetsEqual(config + " trying to over scroll with offset should be no-op", 91999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar before, mLayoutManager.collectChildCoordinates()); 92999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // try offsetting some visible children 93999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int testCount = 10; 94999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar while (testCount-- > 0) { 95999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // get middle child 96999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2); 97999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int position = mRecyclerView.getChildLayoutPosition(child); 98999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int startOffset = config.mReverseLayout ? 99999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 100999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getDecoratedEnd(child) 101999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 102999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getStartAfterPadding(); 103999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2 104999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar : startOffset / 2; 105999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(1); 106999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollToPositionWithOffset(position, scrollOffset); 107999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 108999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int finalOffset = config.mReverseLayout ? 109999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 110999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getDecoratedEnd(child) 111999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 112999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar .getStartAfterPadding(); 113999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(config + " scroll with offset on a visible child should work fine " + 114999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar " offset:" + finalOffset + " , existing offset:" + startOffset + ", " 115999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + "child " + position, 116999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollOffset, finalOffset); 117999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 118999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 119999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // try scrolling to invisible children 120999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar testCount = 10; 121999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // we test above and below, one by one 122999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int offsetMultiplier = -1; 123999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar while (testCount-- > 0) { 124999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final TargetTuple target = findInvisibleTarget(config); 125999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final String logPrefix = config + " " + target; 126999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(1); 127999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int offset = offsetMultiplier 128999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3; 129999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar scrollToPositionWithOffset(target.mPosition, offset); 130999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 131999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final View child = mLayoutManager.findViewByPosition(target.mPosition); 132999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset 133999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + " should layout it", child); 134999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Rect bounds = mLayoutManager.getViewBounds(child); 135999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (DEBUG) { 136999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in " 137999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + layoutBounds + " with offset " + offset); 138999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 139999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 140999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (config.mReverseLayout) { 141999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse " 142999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + "layout, its end should align with recycler view's end - offset", 143999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar orientationHelper.getEndAfterPadding() - offset, 144999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar orientationHelper.getDecoratedEnd(child) 145999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 146999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 147999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals( 148999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar logPrefix + " when scrolling with offset to an invisible child in normal" 149999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + " layout its start should align with recycler view's start + " 150999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + "offset", 151999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar orientationHelper.getStartAfterPadding() + offset, 152999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar orientationHelper.getDecoratedStart(child) 153999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 154999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 155999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar offsetMultiplier *= -1; 156999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 157999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 158999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 159999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Test 160999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void getFirstLastChildrenTest() throws Throwable { 161999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final Config config = ((Config) mConfig.clone()).itemCount(300); 162999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar setupByConfig(config, true); 163999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 164999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 165999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 166999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 167999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 168999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 169999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 170999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 171999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 172999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals( 173999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 174999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 175999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 176999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 177999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 178999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 179999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 180999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 181999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 182999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar assertEquals( 183999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 184999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 185999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 186999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar ); 187999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 188999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }; 189999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(viewInBoundsTest); 190999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 191999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // case 192999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 193999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 194999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 195999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 196999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 197999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 198999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 199999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 200999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 201999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(viewInBoundsTest); 202999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Thread.sleep(400); 203999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 204999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // delete all items 205999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(2); 206999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 207999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 208999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // test empty case 209999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(viewInBoundsTest); 210999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // set a new adapter with huge items to test full bounds check 211999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.expectLayouts(1); 212999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 213999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 214999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 215999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 216999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int position) { 217999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar super.onBindViewHolder(holder, position); 218999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (config.mOrientation == HORIZONTAL) { 219999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 220999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 221999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 222999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 223999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 224999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }; 225999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(new Runnable() { 226999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar @Override 227999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar public void run() { 228999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mRecyclerView.setAdapter(newAdapter); 229999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 230999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar }); 231999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar mLayoutManager.waitForLayout(2); 232999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar runTestOnUiThread(viewInBoundsTest); 233999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 234999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 2354510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Test 2364510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void dontRecycleViewsTranslatedOutOfBoundsFromStart() throws Throwable { 2374510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final Config config = ((Config) mConfig.clone()).itemCount(1000); 2384510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar setupByConfig(config, true); 2394510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar mLayoutManager.expectLayouts(1); 2404510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollToPosition(500); 2414510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar mLayoutManager.waitForLayout(2); 2424510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(500); 2434510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar OrientationHelper helper = mLayoutManager.mOrientationHelper; 2444510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar int gap = helper.getDecoratedStart(vh.itemView); 2454510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(gap); 2464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar gap = helper.getDecoratedStart(vh.itemView); 2474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat("test sanity", gap, is(0)); 2484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 2494510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final int size = helper.getDecoratedMeasurement(vh.itemView); 2504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView); 2514510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar runTestOnUiThread(new Runnable() { 2524510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Override 2534510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void run() { 2544510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mConfig.mOrientation == HORIZONTAL) { 2554510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar ViewCompat.setTranslationX(vh.itemView, size * 2); 2564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } else { 2574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar ViewCompat.setTranslationY(vh.itemView, size * 2); 2584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 2594510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 2604510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar }); 2614510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(size * 2); 2624510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), not(hasItem(sameInstance(vh.itemView)))); 2634510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.itemView.getParent(), is((ViewParent) mRecyclerView)); 2644510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.getAdapterPosition(), is(500)); 2654510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(size * 2); 2664510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), hasItem(sameInstance(vh.itemView))); 2674510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 2684510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 2694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Test 2704510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void dontRecycleViewsTranslatedOutOfBoundsFromEnd() throws Throwable { 2714510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final Config config = ((Config) mConfig.clone()).itemCount(1000); 2724510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar setupByConfig(config, true); 2734510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar mLayoutManager.expectLayouts(1); 2744510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollToPosition(500); 2754510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar mLayoutManager.waitForLayout(2); 2764510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(500); 2774510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar OrientationHelper helper = mLayoutManager.mOrientationHelper; 2784510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar int gap = helper.getEnd() - helper.getDecoratedEnd(vh.itemView); 2794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(-gap); 2804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar gap = helper.getEnd() - helper.getDecoratedEnd(vh.itemView); 2814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat("test sanity", gap, is(0)); 2824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar 2834510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar final int size = helper.getDecoratedMeasurement(vh.itemView); 2844510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView); 2854510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar runTestOnUiThread(new Runnable() { 2864510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar @Override 2874510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar public void run() { 2884510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar if (mConfig.mOrientation == HORIZONTAL) { 2894510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar ViewCompat.setTranslationX(vh.itemView, -size * 2); 2904510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } else { 2914510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar ViewCompat.setTranslationY(vh.itemView, -size * 2); 2924510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 2934510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 2944510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar }); 2954510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(-size * 2); 2964510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), not(hasItem(sameInstance(vh.itemView)))); 2974510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.itemView.getParent(), is((ViewParent) mRecyclerView)); 2984510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(vh.getAdapterPosition(), is(500)); 2994510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar scrollBy(-size * 2); 3004510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar assertThat(collector.getDetached(), hasItem(sameInstance(vh.itemView))); 3014510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar } 302999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar 303999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar private TargetTuple findInvisibleTarget(Config config) { 304999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE; 305999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 306999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar View child = mLayoutManager.getChildAt(i); 307999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar int position = mRecyclerView.getChildLayoutPosition(child); 308999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position < minPosition) { 309999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar minPosition = position; 310999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 311999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (position > maxPosition) { 312999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar maxPosition = position; 313999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 314999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 315999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int tailTarget = maxPosition + 316999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2; 317999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int headTarget = minPosition / 2; 318999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int target; 319999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar // where will the child come from ? 320999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar final int itemLayoutDirection; 321999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) { 322999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar target = tailTarget; 323999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END; 324999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } else { 325999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar target = headTarget; 326999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START; 327999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 328999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar if (DEBUG) { 329999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar Log.d(TAG, 330999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition); 331999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 332999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar return new TargetTuple(target, itemLayoutDirection); 333999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar } 334999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyar} 335