1/* 2 * Copyright (C) 2015 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 android.support.v7.widget; 18 19import org.junit.Test; 20import org.junit.runner.RunWith; 21import org.junit.runners.Parameterized; 22 23import android.graphics.Rect; 24import android.test.suitebuilder.annotation.MediumTest; 25 26import java.util.ArrayList; 27import java.util.List; 28import java.util.Map; 29 30import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE; 31import static org.junit.Assert.assertNull; 32 33@RunWith(Parameterized.class) 34@MediumTest 35public class StaggeredGridLayoutManagerGapTest extends BaseStaggeredGridLayoutManagerTest { 36 private final Config mConfig; 37 private final int mDeletePosition; 38 private final int mDeleteCount; 39 40 public StaggeredGridLayoutManagerGapTest(Config config, int deletePosition, int deleteCount) { 41 mConfig = config; 42 mDeletePosition = deletePosition; 43 mDeleteCount = deleteCount; 44 } 45 46 @Parameterized.Parameters(name = "config={0} deletePos={1} deleteCount={2}") 47 public static List<Object[]> getParams() throws CloneNotSupportedException { 48 List<Config> variations = createBaseVariations(); 49 List<Object[]> params = new ArrayList<>(); 50 for (Config config : variations) { 51 for (int deleteCount = 1; deleteCount < config.mSpanCount * 2; deleteCount++) { 52 for (int deletePosition = config.mSpanCount - 1; 53 deletePosition < config.mSpanCount + 2; deletePosition++) { 54 params.add(new Object[]{config.clone(), deletePosition, deleteCount}); 55 } 56 } 57 } 58 return params; 59 } 60 61 @Test 62 public void gapAtTheBeginningOfTheListTest() throws Throwable { 63 if (mConfig.mSpanCount < 2 || mConfig.mGapStrategy == GAP_HANDLING_NONE) { 64 return; 65 } 66 if (mConfig.mItemCount < 100) { 67 mConfig.itemCount(100); 68 } 69 setupByConfig(mConfig); 70 final RecyclerView.Adapter adapter = mAdapter; 71 waitFirstLayout(); 72 // scroll far away 73 smoothScrollToPosition(mConfig.mItemCount / 2); 74 checkForMainThreadException(); 75 // assert to be deleted child is not visible 76 assertNull(" test sanity, to be deleted child should be invisible", 77 mRecyclerView.findViewHolderForLayoutPosition(mDeletePosition)); 78 // delete the child and notify 79 mAdapter.deleteAndNotify(mDeletePosition, mDeleteCount); 80 getInstrumentation().waitForIdleSync(); 81 mLayoutManager.expectLayouts(1); 82 smoothScrollToPosition(0); 83 mLayoutManager.waitForLayout(2); 84 checkForMainThreadException(); 85 // due to data changes, first item may become visible before others which will cause 86 // smooth scrolling to stop. Triggering it twice more is a naive hack. 87 // Until we have time to consider it as a bug, this is the only workaround. 88 smoothScrollToPosition(0); 89 Thread.sleep(500); 90 checkForMainThreadException(); 91 smoothScrollToPosition(0); 92 Thread.sleep(500); 93 checkForMainThreadException(); 94 // some animations should happen and we should recover layout 95 final Map<Item, Rect> actualCoords = mLayoutManager.collectChildCoordinates(); 96 97 // now layout another RV with same adapter 98 removeRecyclerView(); 99 setupByConfig(mConfig); 100 mRecyclerView.setAdapter(adapter);// use same adapter so that items can be matched 101 waitFirstLayout(); 102 final Map<Item, Rect> desiredCoords = mLayoutManager.collectChildCoordinates(); 103 assertRectSetsEqual(" when an item from the start of the list is deleted, " 104 + "layout should recover the state once scrolling is stopped", 105 desiredCoords, actualCoords); 106 checkForMainThreadException(); 107 } 108} 109