1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.recyclerview.widget; 18 19import static androidx.recyclerview.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE; 20 21import static org.junit.Assert.assertNull; 22 23import android.graphics.Rect; 24import android.support.test.filters.LargeTest; 25 26import org.junit.Test; 27import org.junit.runner.RunWith; 28import org.junit.runners.Parameterized; 29 30import java.util.ArrayList; 31import java.util.List; 32import java.util.Map; 33 34@RunWith(Parameterized.class) 35@LargeTest 36public class StaggeredGridLayoutManagerGapTest extends BaseStaggeredGridLayoutManagerTest { 37 private final Config mConfig; 38 private final int mDeletePosition; 39 private final int mDeleteCount; 40 41 public StaggeredGridLayoutManagerGapTest(Config config, int deletePosition, int deleteCount) { 42 mConfig = config; 43 mDeletePosition = deletePosition; 44 mDeleteCount = deleteCount; 45 } 46 47 @Parameterized.Parameters(name = "config={0},deletePos={1},deleteCount={2}") 48 public static List<Object[]> getParams() throws CloneNotSupportedException { 49 List<Config> variations = createBaseVariations(); 50 List<Object[]> params = new ArrayList<>(); 51 for (Config config : variations) { 52 for (int deleteCount = 1; deleteCount < config.mSpanCount * 2; deleteCount++) { 53 for (int deletePosition = config.mSpanCount - 1; 54 deletePosition < config.mSpanCount + 2; deletePosition++) { 55 params.add(new Object[]{config.clone(), deletePosition, deleteCount}); 56 } 57 } 58 } 59 return params; 60 } 61 62 @Test 63 public void gapAtTheBeginningOfTheListTest() throws Throwable { 64 if (mConfig.mSpanCount < 2 || mConfig.mGapStrategy == GAP_HANDLING_NONE) { 65 return; 66 } 67 if (mConfig.mItemCount < 100) { 68 mConfig.itemCount(100); 69 } 70 setupByConfig(mConfig); 71 final RecyclerView.Adapter adapter = mAdapter; 72 waitFirstLayout(); 73 // scroll far away 74 smoothScrollToPosition(mConfig.mItemCount / 2); 75 checkForMainThreadException(); 76 // assert to be deleted child is not visible 77 assertNull(" test sanity, to be deleted child should be invisible", 78 mRecyclerView.findViewHolderForLayoutPosition(mDeletePosition)); 79 // delete the child and notify 80 mAdapter.deleteAndNotify(mDeletePosition, mDeleteCount); 81 getInstrumentation().waitForIdleSync(); 82 mLayoutManager.expectLayouts(1); 83 smoothScrollToPosition(0); 84 mLayoutManager.waitForLayout(2); 85 checkForMainThreadException(); 86 // due to data changes, first item may become visible before others which will cause 87 // smooth scrolling to stop. Triggering it twice more is a naive hack. 88 // Until we have time to consider it as a bug, this is the only workaround. 89 smoothScrollToPosition(0); 90 Thread.sleep(500); 91 checkForMainThreadException(); 92 smoothScrollToPosition(0); 93 Thread.sleep(500); 94 checkForMainThreadException(); 95 // some animations should happen and we should recover layout 96 final Map<Item, Rect> actualCoords = mLayoutManager.collectChildCoordinates(); 97 98 // now layout another RV with same adapter 99 removeRecyclerView(); 100 setupByConfig(mConfig); 101 mRecyclerView.setAdapter(adapter);// use same adapter so that items can be matched 102 waitFirstLayout(); 103 final Map<Item, Rect> desiredCoords = mLayoutManager.collectChildCoordinates(); 104 assertRectSetsEqual(" when an item from the start of the list is deleted, " 105 + "layout should recover the state once scrolling is stopped", 106 desiredCoords, actualCoords); 107 checkForMainThreadException(); 108 } 109} 110