1d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar/* 2d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Copyright (C) 2014 The Android Open Source Project 3d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 4d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * you may not use this file except in compliance with the License. 6d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * You may obtain a copy of the License at 7d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 8d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * 10d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * See the License for the specific language governing permissions and 14d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * limitations under the License. 15d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */ 16d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 17d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpackage android.support.v7.widget; 18d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 19e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 20e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 21e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas 22e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertEquals; 23e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertNotNull; 24e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertSame; 25e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertTrue; 260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 27a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat; 28a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityEventCompat; 29a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 30f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest; 318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log; 32d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View; 33504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 34a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent; 35d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 36e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport org.junit.Test; 37e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas 38d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 39d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 40504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 41d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 49f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@MediumTest 50999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpublic class LinearLayoutManagerTest extends BaseLinearLayoutManagerTest { 518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItem() throws Throwable { 541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout( 561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar false), 100, 0); 571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemReverse() throws Throwable { 611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(true), 100, 631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 0); 641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemStackFromEnd() throws Throwable { 681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(false), 100, 701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemStackFromEndAndReverse() throws Throwable { 751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(true), 100, 771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemHorizontal() throws Throwable { 821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout( 841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar false), 100, 0); 851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemReverseHorizontal() throws Throwable { 891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(true), 911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 100, 0); 921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemStackFromEndHorizontal() throws Throwable { 961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(false), 981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 100, 99); 991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 1020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void removeAnchorItemStackFromEndAndReverseHorizontal() throws Throwable { 1031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(true), 100, 1051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar /** 1091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * This tests a regression where predictive animations were not working as expected when the 1101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * first item is removed and there aren't any more items to add from that direction. 1111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * First item refers to the default anchor item. 1121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar */ 1131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void removeAnchorItemTest(final Config config, int adapterSize, 1141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int removePos) throws Throwable { 1151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar config.adapter(new TestAdapter(adapterSize) { 1161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar @Override 1171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 1181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar int position) { 1191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar super.onBindViewHolder(holder, position); 1201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); 1211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (!(lp instanceof ViewGroup.MarginLayoutParams)) { 1221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar lp = new ViewGroup.MarginLayoutParams(0, 0); 1231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar holder.itemView.setLayoutParams(lp); 1241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp; 1261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int maxSize; 1271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar maxSize = mRecyclerView.getWidth(); 129e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas mlp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT; 1301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar maxSize = mRecyclerView.getHeight(); 132e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas mlp.width = ViewGroup.MarginLayoutParams.MATCH_PARENT; 1331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int desiredSize; 1361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (position == removePos) { 1371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // make it large 1381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar desiredSize = maxSize / 4; 1391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // make it small 1411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar desiredSize = maxSize / 8; 1421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.width = desiredSize; 1451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.height = desiredSize; 1471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar }); 1501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar setupByConfig(config, true); 1511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 1521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder toBeRemoved = null; 1531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>(); 1541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = 0; i < childCount; i++) { 1551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View child = mLayoutManager.getChildAt(i); 1561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); 1571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (holder.getAdapterPosition() == removePos) { 1581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar toBeRemoved = holder; 1591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar toBeMoved.add(holder); 1611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertNotNull("test sanity", toBeRemoved); 1641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertEquals("test sanity", childCount - 1, toBeMoved.size()); 1651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator(); 1661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mRecyclerView.setItemAnimator(loggingItemAnimator); 1671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.reset(); 1681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.expectRunPendingAnimationsCall(1); 1691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutManager.expectLayouts(2); 1701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mTestAdapter.deleteAndNotify(removePos, 1); 1711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutManager.waitForLayout(1); 1721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.waitForPendingAnimationsCall(2); 1731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("removed child should receive remove animation", 1741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mRemoveVHs.contains(toBeRemoved)); 1751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (RecyclerView.ViewHolder vh : toBeMoved) { 1761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("view holder should be in moved list", 1771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mMoveVHs.contains(vh)); 1781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>(); 1801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 1811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View child = mLayoutManager.getChildAt(i); 1821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); 1831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (toBeRemoved != holder && !toBeMoved.contains(holder)) { 1841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar newHolders.add(holder); 1851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("some new children should show up for the new space", newHolders.size() > 0); 1881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertEquals("no items should receive animate add since they are not new", 0, 1891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mAddVHs.size()); 1901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (RecyclerView.ViewHolder holder : newHolders) { 1911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("new holder should receive a move animation", 1921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mMoveVHs.contains(holder)); 1931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("control against adding too many children due to bad layout state preparation." 1951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(), 1961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/); 1971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepFocusOnRelayout() throws Throwable { 201310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true); 202310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int center = (mLayoutManager.findLastVisibleItemPosition() 203310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar - mLayoutManager.findFirstVisibleItemPosition()) / 2; 204115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(center); 205310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView); 206959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar requestFocus(vh.itemView, true); 207310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertTrue("view should have the focus", vh.itemView.hasFocus()); 208310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 209310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(2); 210310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int childCountToAdd = mRecyclerView.getChildCount() * 2; 211310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mTestAdapter.addAndNotify(center, childCountToAdd); 212310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar center += childCountToAdd; // offset item 213310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(2); 214310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 215115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForLayoutPosition(center); 216310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertNotNull("focused child should stay in layout", postVH); 217310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 218310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("focused child's screen position should stay unchanged", top, 219310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView)); 220310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 221310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepFullFocusOnResize() throws Throwable { 224542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true); 225542f1260934df280985294eaef1ec8469863281fYigit Boyar } 226542f1260934df280985294eaef1ec8469863281fYigit Boyar 2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepPartialFocusOnResize() throws Throwable { 229542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false); 230542f1260934df280985294eaef1ec8469863281fYigit Boyar } 231542f1260934df280985294eaef1ec8469863281fYigit Boyar 2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepReverseFullFocusOnResize() throws Throwable { 234542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true); 235542f1260934df280985294eaef1ec8469863281fYigit Boyar } 236542f1260934df280985294eaef1ec8469863281fYigit Boyar 2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepReversePartialFocusOnResize() throws Throwable { 239542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false); 240542f1260934df280985294eaef1ec8469863281fYigit Boyar } 241542f1260934df280985294eaef1ec8469863281fYigit Boyar 2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepStackFromEndFullFocusOnResize() throws Throwable { 244542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true); 245542f1260934df280985294eaef1ec8469863281fYigit Boyar } 246542f1260934df280985294eaef1ec8469863281fYigit Boyar 2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void keepStackFromEndPartialFocusOnResize() throws Throwable { 249542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false); 250542f1260934df280985294eaef1ec8469863281fYigit Boyar } 251542f1260934df280985294eaef1ec8469863281fYigit Boyar 252542f1260934df280985294eaef1ec8469863281fYigit Boyar public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable { 253542f1260934df280985294eaef1ec8469863281fYigit Boyar setupByConfig(config, true); 254542f1260934df280985294eaef1ec8469863281fYigit Boyar final int targetPosition; 255542f1260934df280985294eaef1ec8469863281fYigit Boyar if (config.mStackFromEnd) { 256542f1260934df280985294eaef1ec8469863281fYigit Boyar targetPosition = mLayoutManager.findFirstVisibleItemPosition(); 257542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 258542f1260934df280985294eaef1ec8469863281fYigit Boyar targetPosition = mLayoutManager.findLastVisibleItemPosition(); 259542f1260934df280985294eaef1ec8469863281fYigit Boyar } 260542f1260934df280985294eaef1ec8469863281fYigit Boyar final OrientationHelper helper = mLayoutManager.mOrientationHelper; 261542f1260934df280985294eaef1ec8469863281fYigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView 262542f1260934df280985294eaef1ec8469863281fYigit Boyar .findViewHolderForLayoutPosition(targetPosition); 263542f1260934df280985294eaef1ec8469863281fYigit Boyar 264542f1260934df280985294eaef1ec8469863281fYigit Boyar // scroll enough to offset the child 265542f1260934df280985294eaef1ec8469863281fYigit Boyar int startMargin = helper.getDecoratedStart(vh.itemView) - 266542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 267542f1260934df280985294eaef1ec8469863281fYigit Boyar int endMargin = helper.getEndAfterPadding() - 268542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 269542f1260934df280985294eaef1ec8469863281fYigit Boyar Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin); 270959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar requestFocus(vh.itemView, true); 271959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar assertTrue("view should gain the focus", vh.itemView.hasFocus()); 272542f1260934df280985294eaef1ec8469863281fYigit Boyar // scroll enough to offset the child 273542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(vh.itemView) - 274542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 275542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - 276542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 277542f1260934df280985294eaef1ec8469863281fYigit Boyar 278542f1260934df280985294eaef1ec8469863281fYigit Boyar Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin); 279542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0); 280542f1260934df280985294eaef1ec8469863281fYigit Boyar 281542f1260934df280985294eaef1ec8469863281fYigit Boyar int expectedOffset = 0; 282542f1260934df280985294eaef1ec8469863281fYigit Boyar boolean offsetAtStart = false; 283542f1260934df280985294eaef1ec8469863281fYigit Boyar if (!fullyVisible) { 284542f1260934df280985294eaef1ec8469863281fYigit Boyar // move it a bit such that it is no more fully visible 285542f1260934df280985294eaef1ec8469863281fYigit Boyar final int childSize = helper 286542f1260934df280985294eaef1ec8469863281fYigit Boyar .getDecoratedMeasurement(vh.itemView); 287542f1260934df280985294eaef1ec8469863281fYigit Boyar expectedOffset = childSize / 3; 288542f1260934df280985294eaef1ec8469863281fYigit Boyar if (startMargin < endMargin) { 289542f1260934df280985294eaef1ec8469863281fYigit Boyar scrollBy(expectedOffset); 290542f1260934df280985294eaef1ec8469863281fYigit Boyar offsetAtStart = true; 291542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 292542f1260934df280985294eaef1ec8469863281fYigit Boyar scrollBy(-expectedOffset); 293542f1260934df280985294eaef1ec8469863281fYigit Boyar offsetAtStart = false; 294542f1260934df280985294eaef1ec8469863281fYigit Boyar } 295542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(vh.itemView) - 296542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 297542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - 298542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 299542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("test sanity, view should not be fully visible", startMargin < 0 300542f1260934df280985294eaef1ec8469863281fYigit Boyar || endMargin < 0); 301542f1260934df280985294eaef1ec8469863281fYigit Boyar } 302542f1260934df280985294eaef1ec8469863281fYigit Boyar 303542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.expectLayouts(1); 304542f1260934df280985294eaef1ec8469863281fYigit Boyar runTestOnUiThread(new Runnable() { 305542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 306542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 307542f1260934df280985294eaef1ec8469863281fYigit Boyar final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams(); 308542f1260934df280985294eaef1ec8469863281fYigit Boyar if (config.mOrientation == HORIZONTAL) { 309542f1260934df280985294eaef1ec8469863281fYigit Boyar layoutParams.width = mRecyclerView.getWidth() / 2; 310542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 311542f1260934df280985294eaef1ec8469863281fYigit Boyar layoutParams.height = mRecyclerView.getHeight() / 2; 312542f1260934df280985294eaef1ec8469863281fYigit Boyar } 313542f1260934df280985294eaef1ec8469863281fYigit Boyar mRecyclerView.setLayoutParams(layoutParams); 314542f1260934df280985294eaef1ec8469863281fYigit Boyar } 315542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 316542f1260934df280985294eaef1ec8469863281fYigit Boyar Thread.sleep(100); 317542f1260934df280985294eaef1ec8469863281fYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 318542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.waitForLayout(2); 319542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 320542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("view should preserve the focus", vh.itemView.hasFocus()); 321542f1260934df280985294eaef1ec8469863281fYigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView 322542f1260934df280985294eaef1ec8469863281fYigit Boyar .findViewHolderForLayoutPosition(targetPosition); 323542f1260934df280985294eaef1ec8469863281fYigit Boyar assertNotNull("focused child should stay in layout", postVH); 324542f1260934df280985294eaef1ec8469863281fYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 325542f1260934df280985294eaef1ec8469863281fYigit Boyar View focused = postVH.itemView; 326542f1260934df280985294eaef1ec8469863281fYigit Boyar 327542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding(); 328542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused); 329542f1260934df280985294eaef1ec8469863281fYigit Boyar 330542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child should be somewhat visible", 331542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedStart(focused) < helper.getEndAfterPadding() 332542f1260934df280985294eaef1ec8469863281fYigit Boyar && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding()); 333542f1260934df280985294eaef1ec8469863281fYigit Boyar if (fullyVisible) { 334542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child end should stay fully visible", 335542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin >= 0); 336542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child start should stay fully visible", 337542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin >= 0); 338542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 339542f1260934df280985294eaef1ec8469863281fYigit Boyar if (offsetAtStart) { 340542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("start should preserve its offset", startMargin < 0); 341542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("end should be visible", endMargin >= 0); 342542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 343542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("end should preserve its offset", endMargin < 0); 344542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("start should be visible", startMargin >= 0); 345542f1260934df280985294eaef1ec8469863281fYigit Boyar } 346542f1260934df280985294eaef1ec8469863281fYigit Boyar } 347542f1260934df280985294eaef1ec8469863281fYigit Boyar } 348542f1260934df280985294eaef1ec8469863281fYigit Boyar 3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollToPositionWithPredictive() throws Throwable { 3516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET); 3526e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 3536e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(3, 20); 3546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 3556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 3566e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar LinearLayoutManager.INVALID_OFFSET); 3576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 3586e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10); 3596e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 3606e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void recycleDuringAnimations() throws Throwable { 363504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 364504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(300) { 365504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 368504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 369504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType); 370504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 371504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder); 372504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 373504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return testViewHolder; 374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 376310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(300) 377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .adapter(adapter), true); 378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 379504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 380504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 381504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 382504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 383504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int cnt = childCount.decrementAndGet(); 384504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 385504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap); 386504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 387504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 388504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 389504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 390504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 391504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 392504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 393504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 394504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 395504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView); 396504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 397504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 398504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 399504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 400504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 401504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500); 402504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 403504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 404504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 405504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 406504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 407504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 408504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(15); 409504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 410504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 411504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 412504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 413504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 414504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 415504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now trigger lots of add again, followed by a scroll to position 416504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 417504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(5 + (i % 3) * 3, 1); 418504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(25); 419504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 420504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20); 421504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 422504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 423504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 424504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 425504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 426504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void dontRecycleChildrenOnDetach() throws Throwable { 43049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 43149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 43249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 43349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 43449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 43534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar ((ViewGroup)mRecyclerView.getParent()).removeView(mRecyclerView); 43649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 43749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 43849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 43949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 44049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 44149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 4420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void recycleChildrenOnDetach() throws Throwable { 44449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 44549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 44649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 44749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 44849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 44949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 45049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 45149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 45234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar ((ViewGroup)mRecyclerView.getParent()).removeView(mRecyclerView); 45349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 45449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 45549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 45649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 45749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 45849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void scrollAndClear() throws Throwable { 4613d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev setupByConfig(new Config(), true); 4623d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 4633d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertTrue("Children not laid out", mLayoutManager.collectChildCoordinates().size() > 0); 4643d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 4653d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mLayoutManager.expectLayouts(1); 4663d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev runTestOnUiThread(new Runnable() { 4673d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev @Override 4683d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void run() { 4693d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mLayoutManager.scrollToPositionWithOffset(1, 0); 4703d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mTestAdapter.clearOnUIThread(); 4713d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 4723d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev }); 4733d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mLayoutManager.waitForLayout(2); 4743d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 4753d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertEquals("Remaining children", 0, mLayoutManager.collectChildCoordinates().size()); 4763d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 4773d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 4783d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void accessibilityPositions() throws Throwable { 481a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 482a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityDelegateCompat delegateCompat = mRecyclerView 483a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar .getCompatAccessibilityDelegate(); 484a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityEvent event = AccessibilityEvent.obtain(); 485a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar runTestOnUiThread(new Runnable() { 486a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 487a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void run() { 488a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event); 489a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 490a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar }); 491a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityRecordCompat record = AccessibilityEventCompat 492a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar .asRecord(event); 493a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar assertEquals("result should have first position", 494a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar record.getFromIndex(), 495a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mLayoutManager.findFirstVisibleItemPosition()); 496a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar assertEquals("result should have last position", 497a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar record.getToIndex(), 498a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mLayoutManager.findLastVisibleItemPosition()); 499a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 500d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 501