BaseStaggeredGridLayoutManagerTest.java revision bc61003e6a10872c23f0de155456cb2fbeef31a7
10a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpackage android.support.v7.widget; 20a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 30a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.graphics.Rect; 40a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.annotation.Nullable; 50a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.util.Log; 60a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.View; 70a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.ViewGroup; 80a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 90a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.lang.reflect.Field; 100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.ArrayList; 110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Arrays; 120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.HashSet; 130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.LinkedHashMap; 140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.List; 150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Map; 160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.CountDownLatch; 170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.TimeUnit; 180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END; 210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START; 220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; 240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE; 250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL; 260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertEquals; 270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertFalse; 280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertNotNull; 290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertTrue; 300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpublic class BaseStaggeredGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static final boolean DEBUG = false; 340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static final int AVG_ITEM_PER_VIEW = 3; 350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static final String TAG = "StaggeredGridLayoutManagerTest"; 360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar volatile WrappedLayoutManager mLayoutManager; 370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar GridTestAdapter mAdapter; 380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static List<Config> createBaseVariations() { 400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar List<Config> variations = new ArrayList<>(); 410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int spanCount : new int[]{1, 3}) { 440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int gapStrategy : new int[]{GAP_HANDLING_NONE, 450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}) { 464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (boolean wrap : new boolean[]{true, false}) { 474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar variations.add(new Config(orientation, reverseLayout, spanCount, 484143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar gapStrategy).wrap(wrap)); 494143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return variations; 560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static List<Config> addConfigVariation(List<Config> base, String fieldName, 590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Object... variations) 600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Field field = Config.class.getDeclaredField(fieldName); 630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Config config : base) { 640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Object variation : variations) { 650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config newConfig = (Config) config.clone(); 660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar field.set(newConfig, variation); 670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar newConfigs.add(newConfig); 680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return newConfigs; 710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void setupByConfig(Config config) throws Throwable { 740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setupByConfig(config, new GridTestAdapter(config.mItemCount, config.mOrientation)); 750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void setupByConfig(Config config, GridTestAdapter adapter) throws Throwable { 780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mAdapter = adapter; 790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView = new RecyclerView(getActivity()); 800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setAdapter(mAdapter); 810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setHasFixedSize(true); 820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager = new WrappedLayoutManager(config.mSpanCount, 830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar config.mOrientation); 840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.setGapStrategy(config.mGapStrategy); 850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.State state) { 910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams 930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); 940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull("view should have layout params assigned", lp); 950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull("when item offsets are requested, view should have a valid span", 960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.mSpan); 970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 1020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StaggeredGridLayoutManager.LayoutParams getLp(View view) { 1050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); 1060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void waitFirstLayout() throws Throwable { 1090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.expectLayouts(1); 1100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar setRecyclerView(mRecyclerView); 1110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.waitForLayout(2); 1120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar getInstrumentation().waitForIdleSync(); 1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar /** 1160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * enqueues an empty runnable to main thread so that we can be assured it did run 1170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * 1180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * @param count Number of times to run 1190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar */ 1200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void waitForMainThread(int count) throws Throwable { 1210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final AtomicInteger i = new AtomicInteger(count); 1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (i.get() > 0) { 1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar runTestOnUiThread(new Runnable() { 1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 1250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 1260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar i.decrementAndGet(); 1270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 1290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 1330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> after) { 1340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Throwable throwable = null; 1350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 1360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual("NOT " + message, before, after); 1370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throwable = t; 1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(message + " two layout should be different", throwable); 1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 1440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertRectSetsEqual(message, before, after, true); 1450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after, 1480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean strictItemEquality) { 1490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StringBuilder log = new StringBuilder(); 1500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 1510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("checking rectangle equality.\n"); 1520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("total space:" + mLayoutManager.mPrimaryOrientation.getTotalSpace()); 1530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("before:"); 1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("\n").append(entry.getKey().mAdapterIndex).append(":") 1560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(entry.getValue()); 1570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("\nafter:"); 1590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 1600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar log.append("\n").append(entry.getKey().mAdapterIndex).append(":") 1610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(entry.getValue()); 1620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar message += "\n\n" + log.toString(); 1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(message + ": item counts should be equal", before.size() 1660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar , after.size()); 1670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 1680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Item beforeItem = entry.getKey(); 1690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect afterRect = null; 1700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (strictItemEquality) { 1710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect = after.get(beforeItem); 1720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(message + ": Same item should be visible after simple re-layout", 1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 1750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) { 1760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Item afterItem = afterEntry.getKey(); 1770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) { 1780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect = afterEntry.getValue(); 1790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar break; 1800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertNotNull(message + ": Item with same adapter index should be visible " + 1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar "after simple re-layout", 1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 1850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals(message + ": Item should be laid out at the same coordinates", 1870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar entry.getValue(), 1880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar afterRect); 1890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void assertViewPositions(Config config) { 1930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ArrayList<ArrayList<View>> viewsBySpan = mLayoutManager.collectChildrenBySpan(); 1940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 1950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .createOrientationHelper(mLayoutManager, config.mOrientation); 1960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (ArrayList<View> span : viewsBySpan) { 1970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // validate all children's order. first child should have min start mPosition 1980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int count = span.size(); 1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0, j = 1; j < count; i++, j++) { 2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View prev = span.get(i); 2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View next = span.get(j); 2020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertTrue(config + " prev item should be above next item", 2030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar orientationHelper.getDecoratedEnd(prev) <= orientationHelper 2040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getDecoratedStart(next) 2050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ); 2060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected TargetTuple findInvisibleTarget(Config config) { 2120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE; 2130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 2140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = mLayoutManager.getChildAt(i); 2150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int position = mRecyclerView.getChildLayoutPosition(child); 2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position < minPosition) { 2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar minPosition = position; 2180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position > maxPosition) { 2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar maxPosition = position; 2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int tailTarget = maxPosition + (mAdapter.getItemCount() - maxPosition) / 2; 2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int headTarget = minPosition / 2; 2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int target; 2260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // where will the child come from ? 2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int itemLayoutDirection; 2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) { 2290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar target = tailTarget; 2300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END; 2310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar target = headTarget; 2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START; 2340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 2360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, 2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition); 2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return new TargetTuple(target, itemLayoutDirection); 2400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void scrollToPositionWithOffset(final int position, final int offset) 2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throws Throwable { 2440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar runTestOnUiThread(new Runnable() { 2450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 2460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 2500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static class OnLayoutListener { 2530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state) { 2550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 2580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static class VisibleChildren { 2620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] firstVisiblePositions; 2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] firstFullyVisiblePositions; 2660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] lastVisiblePositions; 2680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int[] lastFullyVisiblePositions; 2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View findFirstPartialVisibleClosestToStart; 2720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View findFirstPartialVisibleClosestToEnd; 2730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar VisibleChildren(int spanCount) { 2750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstFullyVisiblePositions = new int[spanCount]; 2760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstVisiblePositions = new int[spanCount]; 2770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastVisiblePositions = new int[spanCount]; 2780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastFullyVisiblePositions = new int[spanCount]; 2790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < spanCount; i++) { 2800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstFullyVisiblePositions[i] = RecyclerView.NO_POSITION; 2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar firstVisiblePositions[i] = RecyclerView.NO_POSITION; 2820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastVisiblePositions[i] = RecyclerView.NO_POSITION; 2830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lastFullyVisiblePositions[i] = RecyclerView.NO_POSITION; 2840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 2880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public boolean equals(Object o) { 2890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (this == o) { 2900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return true; 2910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (o == null || getClass() != o.getClass()) { 2930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar VisibleChildren that = (VisibleChildren) o; 2970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(firstFullyVisiblePositions, that.firstFullyVisiblePositions)) { 2990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (findFirstPartialVisibleClosestToStart 3020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar != null ? !findFirstPartialVisibleClosestToStart 3030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .equals(that.findFirstPartialVisibleClosestToStart) 3040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : that.findFirstPartialVisibleClosestToStart != null) { 3050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(firstVisiblePositions, that.firstVisiblePositions)) { 3080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(lastFullyVisiblePositions, that.lastFullyVisiblePositions)) { 3110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (findFirstPartialVisibleClosestToEnd != null ? !findFirstPartialVisibleClosestToEnd 3140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .equals(that.findFirstPartialVisibleClosestToEnd) 3150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : that.findFirstPartialVisibleClosestToEnd 3160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar != null) { 3170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (!Arrays.equals(lastVisiblePositions, that.lastVisiblePositions)) { 3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return false; 3210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return true; 3240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public int hashCode() { 3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int result = Arrays.hashCode(firstVisiblePositions); 3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + Arrays.hashCode(firstFullyVisiblePositions); 3300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + Arrays.hashCode(lastVisiblePositions); 3310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + Arrays.hashCode(lastFullyVisiblePositions); 3320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + (findFirstPartialVisibleClosestToStart != null 3330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ? findFirstPartialVisibleClosestToStart 3340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .hashCode() : 0); 3350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = 31 * result + (findFirstPartialVisibleClosestToEnd != null 3360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ? findFirstPartialVisibleClosestToEnd 3370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .hashCode() 3380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar : 0); 3390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return result; 3400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 3430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public String toString() { 3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "VisibleChildren{" + 3450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar "firstVisiblePositions=" + Arrays.toString(firstVisiblePositions) + 3460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", firstFullyVisiblePositions=" + Arrays.toString(firstFullyVisiblePositions) + 3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", lastVisiblePositions=" + Arrays.toString(lastVisiblePositions) + 3480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", lastFullyVisiblePositions=" + Arrays.toString(lastFullyVisiblePositions) + 3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", findFirstPartialVisibleClosestToStart=" + 3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewToString(findFirstPartialVisibleClosestToStart) + 3510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ", findFirstPartialVisibleClosestToEnd=" + 3520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewToString(findFirstPartialVisibleClosestToEnd) + 3530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar '}'; 3540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar private String viewToString(View view) { 3570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (view == null) { 3580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return null; 3590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ViewGroup.LayoutParams lp = view.getLayoutParams(); 3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp instanceof RecyclerView.LayoutParams == false) { 3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return System.identityHashCode(view) + "(?)"; 3630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.LayoutParams rvlp = (RecyclerView.LayoutParams) lp; 3650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return System.identityHashCode(view) + "(" + rvlp.getViewAdapterPosition() + ")"; 3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar abstract static class OnBindCallback { 3700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar abstract void onBoundItem(TestViewHolder vh, int position); 3720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean assignRandomSize() { 3740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return true; 3750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar void onCreatedViewHolder(TestViewHolder vh) { 3780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 3800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static class Config implements Cloneable { 3820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar static final int DEFAULT_ITEM_COUNT = 300; 3840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mOrientation = OrientationHelper.VERTICAL; 3860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean mReverseLayout = false; 3880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mSpanCount = 3; 3900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; 3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 3954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean mWrap = false; 3964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 3970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config(int orientation, boolean reverseLayout, int spanCount, int gapStrategy) { 3980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOrientation = orientation; 3990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mReverseLayout = reverseLayout; 4000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSpanCount = spanCount; 4010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mGapStrategy = gapStrategy; 4020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Config() { 4050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config orientation(int orientation) { 4090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOrientation = orientation; 4100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config reverseLayout(boolean reverseLayout) { 4140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mReverseLayout = reverseLayout; 4150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config spanCount(int spanCount) { 4190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSpanCount = spanCount; 4200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Config gapStrategy(int gapStrategy) { 4240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mGapStrategy = gapStrategy; 4250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Config itemCount(int itemCount) { 4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mItemCount = itemCount; 4300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return this; 4310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public Config wrap(boolean wrap) { 4344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mWrap = wrap; 4354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return this; 4364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 4374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 4380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public String toString() { 4400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "[CONFIG:" + 4410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " span:" + mSpanCount + "," + 4420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " orientation:" + (mOrientation == HORIZONTAL ? "horz," : "vert,") + 4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " reverse:" + (mReverseLayout ? "T" : "F") + 4440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " itemCount:" + mItemCount + 4454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar " wrapContent:" + mWrap + 4460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar " gap strategy: " + gapStrategyName(mGapStrategy); 4470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected static String gapStrategyName(int gapStrategy) { 4500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar switch (gapStrategy) { 4510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar case GAP_HANDLING_NONE: 4520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "none"; 4530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar case GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS: 4540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "move spans"; 4550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return "gap strategy: unknown"; 4570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Object clone() throws CloneNotSupportedException { 4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return super.clone(); 4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar class WrappedLayoutManager extends StaggeredGridLayoutManager { 4660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar CountDownLatch layoutLatch; 4680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar OnLayoutListener mOnLayoutListener; 4690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // gradle does not yet let us customize manifest for tests which is necessary to test RTL. 4700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // until bug is fixed, we'll fake it. 4710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // public issue id: 57819 4720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Boolean mFakeRTL; 4730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 4750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean isLayoutRTL() { 4760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL; 4770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void expectLayouts(int count) { 4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch = new CountDownLatch(count); 4810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 483bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 4840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar waitForLayout(timeout * (DEBUG ? 1000 : 1), TimeUnit.SECONDS); 4850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 487bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 4880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch.await(timeout, timeUnit); 489bc61003e6a10872c23f0de155456cb2fbeef31a7Yigit Boyar checkForMainThreadException(); 4900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 4910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 0, layoutLatch.getCount()); 4920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 4950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 4960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 4970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 4980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 4990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 5000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 5010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String before; 5020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (DEBUG) { 5030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before = layoutToString("before"); 5040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 5050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar before = "enable DEBUG"; 5060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 5080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnLayoutListener != null) { 5090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnLayoutListener.before(recycler, state); 5100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.onLayoutChildren(recycler, state); 5120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnLayoutListener != null) { 5130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnLayoutListener.after(recycler, state); 5140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(before); 5160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 5170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 5180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutLatch.countDown(); 5210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 5240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) { 5250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 5260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int result = super.scrollBy(dt, recycler, state); 5270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(); 5280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return result; 5290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 5300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 5310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return 0; 5340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public WrappedLayoutManager(int spanCount, int orientation) { 5370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super(spanCount, orientation); 5380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ArrayList<ArrayList<View>> collectChildrenBySpan() { 5410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar ArrayList<ArrayList<View>> viewsBySpan = new ArrayList<ArrayList<View>>(); 5420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < getSpanCount(); i++) { 5430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewsBySpan.add(new ArrayList<View>()); 5440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < getChildCount(); i++) { 5460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View view = getChildAt(i); 5470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp 5480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar = (LayoutParams) view 5490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 5500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar viewsBySpan.get(lp.mSpan.mIndex).add(view); 5510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return viewsBySpan; 5530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Nullable 5560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 5570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler, 5580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.State state) { 5590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View result = null; 5600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 5610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar result = super.onFocusSearchFailed(focused, direction, recycler, state); 5620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(); 5630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable t) { 5640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(t); 5650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return result; 5670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Rect getViewBounds(View view) { 5700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (getOrientation() == HORIZONTAL) { 5710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return new Rect( 5720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(view), 5730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedEnd(view), 5750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 5760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 5770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return new Rect( 5780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedStart(view), 5790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(view), 5800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 5810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedEnd(view)); 5820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 5850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public String getBoundsLog() { 5860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StringBuilder sb = new StringBuilder(); 5870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("view bounds:[start:").append(mPrimaryOrientation.getStartAfterPadding()) 5880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(",").append(" end").append(mPrimaryOrientation.getEndAfterPadding()); 5890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("\nchildren bounds\n"); 5900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childCount = getChildCount(); 5910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < childCount; i++) { 5920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = getChildAt(i); 5930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 5940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append("[").append("start:").append( 5950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(child)).append(", end:") 5960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .append(mPrimaryOrientation.getDecoratedEnd(child)).append("]\n"); 5970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 5980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return sb.toString(); 5990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 6020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int childCount = getChildCount(); 6030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(getSpanCount()); 6040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int start = mPrimaryOrientation.getStartAfterPadding(); 6050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int end = mPrimaryOrientation.getEndAfterPadding(); 6060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < childCount; i++) { 6070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = getChildAt(i); 6080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childStart = mPrimaryOrientation.getDecoratedStart(child); 6090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); 6100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 6110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 6120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (hidden) { 6130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 6140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int position = getPosition(child); 6160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int span = getLp(child).getSpanIndex(); 6170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (fullyVisible) { 6180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position < visibleChildren.firstFullyVisiblePositions[span] || 6190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstFullyVisiblePositions[span] 6200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar == RecyclerView.NO_POSITION) { 6210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstFullyVisiblePositions[span] = position; 6220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position > visibleChildren.lastFullyVisiblePositions[span]) { 6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.lastFullyVisiblePositions[span] = position; 6260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position < visibleChildren.firstVisiblePositions[span] || 6300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstVisiblePositions[span] == RecyclerView.NO_POSITION) { 6310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.firstVisiblePositions[span] = position; 6320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (position > visibleChildren.lastVisiblePositions[span]) { 6350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.lastVisiblePositions[span] = position; 6360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (visibleChildren.findFirstPartialVisibleClosestToStart == null) { 6380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.findFirstPartialVisibleClosestToStart = child; 6390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar visibleChildren.findFirstPartialVisibleClosestToEnd = child; 6410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return visibleChildren; 6430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 6460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 6470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar runTestOnUiThread(new Runnable() { 6480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 6490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void run() { 6500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int childCount = getChildCount(); 6510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < childCount; i++) { 6520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar View child = getChildAt(i); 6530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // do it if and only if child is visible 6540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (child.getRight() < 0 || child.getBottom() < 0 || 6550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar child.getLeft() >= getWidth() || child.getTop() >= getHeight()) { 6560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // invisible children may be drawn in cases like scrolling so we should 6570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // ignore them 6580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 6590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp = (LayoutParams) child 6610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 6620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 6630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar items.put(vh.mBoundItem, getViewBounds(child)); 6640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar }); 6670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return items; 6680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void setFakeRtl(Boolean fakeRtl) { 6720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFakeRTL = fakeRtl; 6730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar try { 6740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar requestLayoutOnUIThread(mRecyclerView); 6750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } catch (Throwable throwable) { 6760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar postExceptionToInstrumentation(throwable); 6770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar String layoutToString(String hint) { 6810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar StringBuilder sb = new StringBuilder(); 6820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append("LAYOUT POSITIONS AND INDICES ").append(hint).append("\n"); 6830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (int i = 0; i < getChildCount(); i++) { 6840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final View view = getChildAt(i); 6850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); 6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar sb.append(String.format("index: %d pos: %d top: %d bottom: %d span: %d isFull:%s", 6870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar i, getPosition(view), 6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedStart(view), 6890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mPrimaryOrientation.getDecoratedEnd(view), 6900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutParams.getSpanIndex(), layoutParams.isFullSpan())).append("\n"); 6910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return sb.toString(); 6930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void validateChildren() { 6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar validateChildren(null); 6970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 6980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 6990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar private void validateChildren(String msg) { 7000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (getChildCount() == 0 || mRecyclerView.mState.isPreLayout()) { 7010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return; 7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int dir = mShouldReverseLayout ? -1 : 1; 7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int i = 0; 7050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int pos = -1; 7060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (i < getChildCount()) { 7070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 7080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp.isItemRemoved()) { 7090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar i++; 7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar pos = getPosition(getChildAt(i)); 7130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar break; 7140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (pos == -1) { 7160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return; 7170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar while (++i < getChildCount()) { 7190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 7200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp.isItemRemoved()) { 7210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar continue; 7220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar pos += dir; 7240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (getPosition(getChildAt(i)) != pos) { 7250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar throw new RuntimeException("INVALID POSITION FOR CHILD " + i + "\n" + 7260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar layoutToString("ERROR") + "\n msg:" + msg); 7270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar class GridTestAdapter extends TestAdapter { 7330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mOrientation; 7350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mRecyclerViewWidth; 7360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int mRecyclerViewHeight; 7370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Integer mSizeReference = null; 7380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar // original ids of items that should be full span 7400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar HashSet<Integer> mFullSpanItems = new HashSet<Integer>(); 7410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected boolean mViewsHaveEqualSize = false; // size in the scrollable direction 7430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected OnBindCallback mOnBindCallback; 7450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar GridTestAdapter(int count, int orientation) { 7470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super(count); 7480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOrientation = orientation; 7490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 7530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int viewType) { 7540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerViewWidth = parent.getWidth(); 7550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mRecyclerViewHeight = parent.getHeight(); 7560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar TestViewHolder vh = super.onCreateViewHolder(parent, viewType); 7570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnBindCallback != null) { 7580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnBindCallback.onCreatedViewHolder(vh); 7590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return vh; 7610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 7650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mFullSpanItems.size() > 0) { 7660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar HashSet<Integer> old = mFullSpanItems; 7670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems = new HashSet<Integer>(); 7680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar for (Integer i : old) { 7690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (i < start) { 7700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems.add(i); 7710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else if (offset > 0 || (start + Math.abs(offset)) <= i) { 7720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems.add(i + offset); 7730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else if (DEBUG) { 7740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Log.d(TAG, "removed full span item " + i); 7750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.offsetOriginalIndices(start, offset); 7790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected void moveInUIThread(int from, int to) { 7830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar boolean setAsFullSpanAgain = mFullSpanItems.contains(from); 7840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.moveInUIThread(from, to); 7850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (setAsFullSpanAgain) { 7860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mFullSpanItems.add(to); 7870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 7900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Override 7910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 7920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar int position) { 7930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mSizeReference == null) { 7940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSizeReference = mOrientation == OrientationHelper.HORIZONTAL ? mRecyclerViewWidth 7950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar / AVG_ITEM_PER_VIEW : mRecyclerViewHeight / AVG_ITEM_PER_VIEW; 7960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 7970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar super.onBindViewHolder(holder, position); 7980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar Item item = mItems.get(position); 7990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.itemView 8000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar .getLayoutParams(); 8010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (lp instanceof StaggeredGridLayoutManager.LayoutParams) { 8024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ((StaggeredGridLayoutManager.LayoutParams) lp) 8034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar .setFullSpan(mFullSpanItems.contains(item.mAdapterIndex)); 8040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 8054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar StaggeredGridLayoutManager.LayoutParams slp 8064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar = (StaggeredGridLayoutManager.LayoutParams) mLayoutManager 8074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar .generateDefaultLayoutParams(); 8080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setLayoutParams(slp); 8090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar slp.setFullSpan(mFullSpanItems.contains(item.mAdapterIndex)); 8100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp = slp; 8110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 8130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnBindCallback == null || mOnBindCallback.assignRandomSize()) { 8140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar final int minSize = mViewsHaveEqualSize ? mSizeReference : 8150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mSizeReference + 20 * (item.mId % 10); 8160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOrientation == OrientationHelper.HORIZONTAL) { 8170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setMinimumWidth(minSize); 8180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } else { 8190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar holder.itemView.setMinimumHeight(minSize); 8200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.topMargin = 3; 8220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.leftMargin = 5; 8230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.rightMargin = 7; 8240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar lp.bottomMargin = 9; 8250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar if (mOnBindCallback != null) { 8280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar mOnBindCallback.onBoundItem(holder, position); 8290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar} 833