GridLayoutManager.java revision 42e9353bb9eb2747247e30e3612b227945acfd16
1b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar/* 2b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Copyright (C) 2014 The Android Open Source Project 3b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 4b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * you may not use this file except in compliance with the License. 6b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * You may obtain a copy of the License at 7b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 8b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 10b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * See the License for the specific languag`e governing permissions and 14b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * limitations under the License. 15b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 16b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarpackage android.support.v7.widget; 17b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 18b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.content.Context; 19b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.graphics.Rect; 20a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 21b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.util.AttributeSet; 22b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.util.Log; 239ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyarimport android.util.SparseIntArray; 24b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.view.View; 25b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.view.ViewGroup; 26b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 27b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport java.util.Arrays; 28b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 29b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar/** 30b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid. 31b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 32b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * By default, each item occupies 1 span. You can change it by providing a custom 33b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}. 34b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 35b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarpublic class GridLayoutManager extends LinearLayoutManager { 36b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 37b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private static final boolean DEBUG = false; 38b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private static final String TAG = "GridLayoutManager"; 39b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final int DEFAULT_SPAN_COUNT = -1; 40061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 41061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * The measure spec for the scroll direction. 42061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 43061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar static final int MAIN_DIR_SPEC = 44061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 45061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 46b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int mSpanCount = DEFAULT_SPAN_COUNT; 47b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 48b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * The size of each span 49b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 50b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int mSizePerSpan; 51b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 52b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Temporary array to keep views in layoutChunk method 53b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 54b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View[] mSet; 559ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray(); 56061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray(); 57b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup(); 585e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // re-used variable to acquire decor insets from RecyclerView 595e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar final Rect mDecorInsets = new Rect(); 605e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar 615f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar /** 625f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * Creates a vertical GridLayoutManager 635f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * 645f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param context Current context, will be used to access resources. 655f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param spanCount The number of columns in the grid 665f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar */ 67b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public GridLayoutManager(Context context, int spanCount) { 68b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(context); 69b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar setSpanCount(spanCount); 70b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 71b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 725f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar /** 735f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param context Current context, will be used to access resources. 745f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param spanCount The number of columns or rows in the grid 755f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link 765f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * #VERTICAL}. 775f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param reverseLayout When set to true, layouts from end to start. 785f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar */ 79b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public GridLayoutManager(Context context, int spanCount, int orientation, 80b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar boolean reverseLayout) { 81b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(context, orientation, reverseLayout); 82b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar setSpanCount(spanCount); 83b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 84b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 855f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar /** 865f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * stackFromEnd is not supported by GridLayoutManager. Consider using 875f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * {@link #setReverseLayout(boolean)}. 885f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar */ 89b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 90061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void setStackFromEnd(boolean stackFromEnd) { 915f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar if (stackFromEnd) { 925f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar throw new UnsupportedOperationException( 935f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar "GridLayoutManager does not support stack from end." 945f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar + " Consider using reverse layout"); 955f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar } 965f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar super.setStackFromEnd(false); 97061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 98061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 99061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 100a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getRowCountForAccessibility(RecyclerView.Recycler recycler, 101a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state) { 102a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == HORIZONTAL) { 103a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanCount; 104a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 105a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (state.getItemCount() < 1) { 106a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 107a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 108a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); 109a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 110a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 111a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 112a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, 113a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state) { 114a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == VERTICAL) { 115a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanCount; 116a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 117a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (state.getItemCount() < 1) { 118a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 119a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 120a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); 121a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 122a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 123a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 124a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, 125a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { 126a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar ViewGroup.LayoutParams lp = host.getLayoutParams(); 127a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (!(lp instanceof LayoutParams)) { 128a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar super.onInitializeAccessibilityNodeInfoForItem(host, info); 129a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return; 130a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 131a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar LayoutParams glp = (LayoutParams) lp; 132a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewPosition()); 133a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == HORIZONTAL) { 134a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( 135a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar glp.getSpanIndex(), glp.getSpanSize(), 136a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar spanGroupIndex, 1, 137a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); 138a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else { // VERTICAL 139a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( 140a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar spanGroupIndex , 1, 141a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar glp.getSpanIndex(), glp.getSpanSize(), 142a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); 143a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 144a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 145a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 146a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 147b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 1489ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (state.isPreLayout()) { 149061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar cachePreLayoutSpanMapping(); 1509ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 151b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super.onLayoutChildren(recycler, state); 152b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG) { 153b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar validateChildOrder(); 154b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 155061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar clearPreLayoutSpanMappingCache(); 156061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 157061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 158061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private void clearPreLayoutSpanMappingCache() { 1599ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar mPreLayoutSpanSizeCache.clear(); 160061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mPreLayoutSpanIndexCache.clear(); 1619ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 1629ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 163061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private void cachePreLayoutSpanMapping() { 1649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int childCount = getChildCount(); 165061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < childCount; i++) { 1669ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 167061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int viewPosition = lp.getViewPosition(); 168061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize()); 169061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex()); 1709ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 171b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 172b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 173b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 174061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { 175061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 176061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 177061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 178061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 179061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsChanged(RecyclerView recyclerView) { 180061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 181061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 182061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 183061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 184061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { 185061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 186061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 187061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 188061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 189061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { 190061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 191061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 192061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 193061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 194061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { 195061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 196061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 197061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 198061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 199b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public RecyclerView.LayoutParams generateDefaultLayoutParams() { 200b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 201b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar ViewGroup.LayoutParams.WRAP_CONTENT); 202b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 203b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 204b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 205b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { 206b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams(c, attrs); 207b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 208b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 209b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 210b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 211b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (lp instanceof ViewGroup.MarginLayoutParams) { 212b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams((ViewGroup.MarginLayoutParams) lp); 213b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 214b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams(lp); 215b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 216b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 217b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 218b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 219b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { 220b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return lp instanceof LayoutParams; 221b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 222b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 223b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 224b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Sets the source to get the number of spans occupied by each item in the adapter. 225b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 226b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans 227061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * occupied by each item 228b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 229b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) { 230b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSpanSizeLookup = spanSizeLookup; 231b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 232b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 233b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 234b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager. 235b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 236b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The current {@link SpanSizeLookup} used by the GridLayoutManager. 237b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 238b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public SpanSizeLookup getSpanSizeLookup() { 239b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanSizeLookup; 240b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 241b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 242b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private void updateMeasurements() { 243b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int totalSpace; 244b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (getOrientation() == VERTICAL) { 245b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); 246b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 247b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar totalSpace = getHeight() - getPaddingBottom() - getPaddingTop(); 248b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 249b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSizePerSpan = totalSpace / mSpanCount; 250b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 251b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 252b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 2539ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) { 2549ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar super.onAnchorReady(state, anchorInfo); 255b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar updateMeasurements(); 256061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (state.getItemCount() > 0 && !state.isPreLayout()) { 2579ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar ensureAnchorIsInFirstSpan(anchorInfo); 2589ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 2599ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (mSet == null || mSet.length != mSpanCount) { 2609ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar mSet = new View[mSpanCount]; 2619ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 2629ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 2639ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 2649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) { 265061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); 26673304eff156157f62075215e795e774803a6f96aYigit Boyar while (span > 0 && anchorInfo.mPosition > 0) { 267061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar anchorInfo.mPosition--; 268061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); 269061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 270061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 271061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 272a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state, 273a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int viewPosition) { 274a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (!state.isPreLayout()) { 275a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount); 276a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 277a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); 278a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (adapterPosition == -1) { 279a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (DEBUG) { 280a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar throw new RuntimeException("Cannot find span group index for position " 281a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar + viewPosition); 282a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 283a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); 284a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 285a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 286a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount); 287a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 288a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 289061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { 290061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!state.isPreLayout()) { 291061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount); 292061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 293061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int cached = mPreLayoutSpanIndexCache.get(pos, -1); 294061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (cached != -1) { 295061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return cached; 296061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 297061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); 298061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (adapterPosition == -1) { 299061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (DEBUG) { 300061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar throw new RuntimeException("Cannot find span index for pre layout position. It is" 301061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar + " not cached, not in the adapter. Pos:" + pos); 302061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 303061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. It is" 304061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar + " not cached, not in the adapter. Pos:" + pos); 305061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return 0; 306b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 307061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount); 3089ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3099ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 3109ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { 3119ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (!state.isPreLayout()) { 3129ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mSpanSizeLookup.getSpanSize(pos); 313b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 3149ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int cached = mPreLayoutSpanSizeCache.get(pos, -1); 3159ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (cached != -1) { 3169ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return cached; 3179ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3189ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); 3199ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (adapterPosition == -1) { 3209ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (DEBUG) { 3219ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar throw new RuntimeException("Cannot find span size for pre layout position. It is" 3229ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar + " not cached, not in the adapter. Pos:" + pos); 3239ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3249ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. It is" 3259ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar + " not cached, not in the adapter. Pos:" + pos); 3269ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return 1; 3279ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3289ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mSpanSizeLookup.getSpanSize(adapterPosition); 329b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 330b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 331b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 332b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, 333b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutState layoutState, LayoutChunkResult result) { 334061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final boolean layingOutInPrimaryDirection = 335061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; 336b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int count = 0; 337061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int consumedSpanCount = 0; 338b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int remainingSpan = mSpanCount; 339061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!layingOutInPrimaryDirection) { 340061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); 341061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); 342061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar remainingSpan = itemSpanIndex + itemSpanSize; 343061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 344b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { 345b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int pos = layoutState.mCurrentPosition; 3469ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int spanSize = getSpanSize(recycler, state, pos); 34773304eff156157f62075215e795e774803a6f96aYigit Boyar if (spanSize > mSpanCount) { 34873304eff156157f62075215e795e774803a6f96aYigit Boyar throw new IllegalArgumentException("Item at position " + pos + " requires " + 34973304eff156157f62075215e795e774803a6f96aYigit Boyar spanSize + " spans but GridLayoutManager has only " + mSpanCount 35073304eff156157f62075215e795e774803a6f96aYigit Boyar + " spans."); 35173304eff156157f62075215e795e774803a6f96aYigit Boyar } 352b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar remainingSpan -= spanSize; 353b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (remainingSpan < 0) { 354b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar break; // item did not fit into this row or column 355b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 356b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = layoutState.next(recycler); 357b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (view == null) { 358b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar break; 359b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 360061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar consumedSpanCount += spanSize; 361b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSet[count] = view; 362061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar count++; 363b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 364b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 365b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (count == 0) { 366b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mFinished = true; 367b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 368b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 369b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 370b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int maxSize = 0; 371061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 3725e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // we should assign spans before item decor offsets are calculated 373061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); 374061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < count; i++) { 375b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = mSet[i]; 3769ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layoutState.mScrapList == null) { 3779ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layingOutInPrimaryDirection) { 3789ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addView(view); 3799ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } else { 3809ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addView(view, 0); 3819ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 382b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 3839ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layingOutInPrimaryDirection) { 3849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addDisappearingView(view); 3859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } else { 3869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addDisappearingView(view, 0); 3879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 388b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 3899ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 3909ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar int spanSize = getSpanSize(recycler, state, getPosition(view)); 391b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize, 392b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View.MeasureSpec.EXACTLY); 393061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 394b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 395061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height)); 396b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 397061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec); 398b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 399b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar final int size = mOrientationHelper.getDecoratedMeasurement(view); 400b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (size > maxSize) { 401b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar maxSize = size; 402b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 403b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 404afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar 405afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar // views that did not measure the maxSize has to be re-measured 406afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar final int maxMeasureSpec = getMainDirSpec(maxSize); 407afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar for (int i = 0; i < count; i ++) { 408afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar final View view = mSet[i]; 409afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) { 410afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar int spanSize = getSpanSize(recycler, state, getPosition(view)); 411afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize, 412afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar View.MeasureSpec.EXACTLY); 413afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar if (mOrientation == VERTICAL) { 414afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec); 415afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } else { 416afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec); 417afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 418afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 419afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 420afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar 421b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mConsumed = maxSize; 422b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 4235e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar int left = 0, right = 0, top = 0, bottom = 0; 424b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 425b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 426b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = layoutState.mOffset; 427b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = bottom - maxSize; 428b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 429b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = layoutState.mOffset; 430b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + maxSize; 431b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 432b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 433b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 434b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = layoutState.mOffset; 435b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = right - maxSize; 436b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 437b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = layoutState.mOffset; 438b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = left + maxSize; 439b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 440b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 441061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < count; i++) { 442b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = mSet[i]; 443b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutParams params = (LayoutParams) view.getLayoutParams(); 444b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 4455e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar left = getPaddingLeft() + mSizePerSpan * params.mSpanIndex; 446b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); 447b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 4485e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar top = getPaddingTop() + mSizePerSpan * params.mSpanIndex; 449b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); 450b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 451b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // We calculate everything with View's bounding box (which includes decor and margins) 452b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // To calculate correct layout position, we subtract margins. 453b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar layoutDecorated(view, left + params.leftMargin, top + params.topMargin, 454b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right - params.rightMargin, bottom - params.bottomMargin); 455b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG) { 456b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" 457b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" 458b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin) 4595e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize); 460b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 461b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // Consume the available space if the view is not removed OR changed 462b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (params.isItemRemoved() || params.isItemChanged()) { 463b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mIgnoreConsumed = true; 464b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 465b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mFocusable |= view.isFocusable(); 466b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 467b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Arrays.fill(mSet, null); 468b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 469b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private int getMainDirSpec(int dim) { 471061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (dim < 0) { 472061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return MAIN_DIR_SPEC; 473061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } else { 474061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY); 475061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 476061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 477061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 478b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) { 4795e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar calculateItemDecorationsForChild(child, mDecorInsets); 480b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); 4815e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left, 4825e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar lp.rightMargin + mDecorInsets.right); 4835e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top, 4845e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar lp.bottomMargin + mDecorInsets.bottom); 485b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar child.measure(widthSpec, heightSpec); 486b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 487b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 488b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private int updateSpecWithExtra(int spec, int startInset, int endInset) { 489b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (startInset == 0 && endInset == 0) { 490b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return spec; 491b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 492b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar final int mode = View.MeasureSpec.getMode(spec); 493b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) { 494b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return View.MeasureSpec.makeMeasureSpec( 495b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View.MeasureSpec.getSize(spec) - startInset - endInset, mode); 496b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 497b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return spec; 498b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 499b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 5009ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, 501061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int consumedSpanCount, boolean layingOutInPrimaryDirection) { 5025e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar int span, spanDiff, start, end, diff; 5035e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // make sure we traverse from min position to max position 5045e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (layingOutInPrimaryDirection) { 5055e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar start = 0; 5065e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar end = count; 5075e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar diff = 1; 5085e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 5095e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar start = count - 1; 5105e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar end = -1; 5115e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar diff = -1; 5125e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 5135e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span 514061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar span = consumedSpanCount - 1; 5155e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar spanDiff = -1; 5165e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 5175e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar span = 0; 5185e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar spanDiff = 1; 5195e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 5205e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar for (int i = start; i != end; i += diff) { 5215e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar View view = mSet[i]; 5225e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar LayoutParams params = (LayoutParams) view.getLayoutParams(); 5239ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); 5245e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (spanDiff == -1 && params.mSpanSize > 1) { 5255e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar params.mSpanIndex = span - (params.mSpanSize - 1); 5265e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 5275e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar params.mSpanIndex = span; 5285e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 5295e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar span += spanDiff * params.mSpanSize; 5305e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 5315e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 5325e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar 533b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 534b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of spans laid out by this grid. 535b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 536b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans 537b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see #setSpanCount(int) 538b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 539b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanCount() { 540b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanCount; 541b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 542b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 543b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 544b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Sets the number of spans to be laid out. 545b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 546b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns. 547b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows. 548b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 549b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanCount The total number of spans in the grid 550b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see #getSpanCount() 551b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 552b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void setSpanCount(int spanCount) { 553b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (spanCount == mSpanCount) { 554b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 555b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 556b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (spanCount < 1) { 557b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar throw new IllegalArgumentException("Span count should be at least 1. Provided " 558b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + spanCount); 559b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 560b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSpanCount = spanCount; 561061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 562b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 563b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 564b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 565b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * A helper class to provide the number of spans each item occupies. 566b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 567b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Default implementation sets each item to occupy exactly 1 span. 568b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 569b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup) 570b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 571b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static abstract class SpanSizeLookup { 572061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 573061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final SparseIntArray mSpanIndexCache = new SparseIntArray(); 574061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 575061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private boolean mCacheSpanIndices = false; 576061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 577b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 578b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of span occupied by the item at <code>position</code>. 579b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 580b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param position The adapter position of the item 581b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans occupied by the item at the provided position 582b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 583b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar abstract public int getSpanSize(int position); 584b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 585b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 586061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or 587061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * not. By default these values are not cached. If you are not overriding 588061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * {@link #getSpanIndex(int, int)}, you should set this to true for better performance. 589061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * 590061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not. 591061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 592061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) { 593061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mCacheSpanIndices = cacheSpanIndices; 594061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 595061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 596061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 597061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Clears the span index cache. GridLayoutManager automatically calls this method when 598061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * adapter changes occur. 599061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 600061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void invalidateSpanIndexCache() { 601061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanIndexCache.clear(); 602061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 603061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 604061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 605061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not. 606061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * 607061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @return True if results of {@link #getSpanIndex(int, int)} are cached. 608061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 609061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public boolean isSpanIndexCacheEnabled() { 610061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mCacheSpanIndices; 611061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 612061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 613061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int getCachedSpanIndex(int position, int spanCount) { 614061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!mCacheSpanIndices) { 615061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return getSpanIndex(position, spanCount); 616061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 617061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int existing = mSpanIndexCache.get(position, -1); 618061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (existing != -1) { 619061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return existing; 620061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 621061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int value = getSpanIndex(position, spanCount); 622061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanIndexCache.put(position, value); 623061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return value; 624061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 625061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 626061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 627b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the final span index of the provided position. 628b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 629061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * If you have a faster way to calculate span index for your items, you should override 630061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * this method. Otherwise, you should enable span index cache 631061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is 632061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * disabled, default implementation traverses all items from 0 to 633061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * <code>position</code>. When caching is enabled, it calculates from the closest cached 634061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * value before the <code>position</code>. 635061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * <p> 636061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * If you override this method, you need to make sure it is consistent with 637a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for 638061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * each item. It is called only for the reference item and rest of the items 639061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * are assigned to spans based on the reference item. For example, you cannot assign a 640061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * position to span 2 while span 1 is empty. 641b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 642061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Note that span offsets always start with 0 and are not affected by RTL. 643b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 644061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @param position The position of the item 645b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanCount The total number of spans in the grid 646b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The final span position of the item. Should be between 0 (inclusive) and 647b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <code>spanCount</code>(exclusive) 648b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 649b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex(int position, int spanCount) { 650b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int positionSpanSize = getSpanSize(position); 651b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (positionSpanSize == spanCount) { 652b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 0; // quick return for full-span items 653b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 65473304eff156157f62075215e795e774803a6f96aYigit Boyar int span = 0; 655061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int startPos = 0; 656061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar // If caching is enabled, try to jump 657061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (mCacheSpanIndices && mSpanIndexCache.size() > 0) { 658061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int prevKey = findReferenceIndexFromCache(position); 659061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (prevKey >= 0) { 660061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey); 661061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar startPos = prevKey + 1; 662061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 663061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 664061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = startPos; i < position; i++) { 66573304eff156157f62075215e795e774803a6f96aYigit Boyar int size = getSpanSize(i); 666b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span += size; 667b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (span == spanCount) { 668b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span = 0; 669b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else if (span > spanCount) { 670b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // did not fit, moving to next row / column 671b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span = size; 672b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 673b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 674b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (span + positionSpanSize <= spanCount) { 675b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return span; 676b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 677b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 0; 678b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 679061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 680061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int findReferenceIndexFromCache(int position) { 681061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int lo = 0; 682061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int hi = mSpanIndexCache.size() - 1; 683061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 684061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar while (lo <= hi) { 685061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int mid = (lo + hi) >>> 1; 686061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int midVal = mSpanIndexCache.keyAt(mid); 687061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (midVal < position) { 688061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar lo = mid + 1; 689061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } else { 690061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar hi = mid - 1; 691061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 692061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 693061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int index = lo - 1; 694061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (index >= 0 && index < mSpanIndexCache.size()) { 695061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanIndexCache.keyAt(index); 696061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 697061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return -1; 698061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 699a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 700a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar /** 701a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * Returns the index of the group this position belongs. 702a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * <p> 703a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * For example, if grid has 3 columns and each item occupies 1 span, span group index 704a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * for item 1 will be 0, item 5 will be 1. 705a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * 706a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @param adapterPosition The position in adapter 707a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @param spanCount The total number of spans in the grid 708a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @return The index of the span group including the item at the given adapter position 709a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar */ 710a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getSpanGroupIndex(int adapterPosition, int spanCount) { 711a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int span = 0; 712a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int group = 0; 713a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int positionSpanSize = getSpanSize(adapterPosition); 714a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar for (int i = 0; i < adapterPosition; i++) { 715a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int size = getSpanSize(i); 716a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span += size; 717a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (span == spanCount) { 718a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span = 0; 719a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 720a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else if (span > spanCount) { 721a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar // did not fit, moving to next row / column 722a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span = size; 723a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 724a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 725a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 726a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (span + positionSpanSize > spanCount) { 727a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 728a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 729a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return group; 730a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 731b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 732b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 733b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 734b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean supportsPredictiveItemAnimations() { 7359ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mPendingSavedState == null; 736b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 737b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 738b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 739b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span. 740b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 741b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final class DefaultSpanSizeLookup extends SpanSizeLookup { 742061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 743b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 744b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanSize(int position) { 745b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 1; 746b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 747b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 748b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 749b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex(int position, int spanCount) { 750b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return position % spanCount; 751b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 752b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 753b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 754b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 755b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * LayoutParams used by GridLayoutManager. 75642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * <p> 75742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the 75842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is 75942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * expected to fill all of the space given to it. 760b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 761b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static class LayoutParams extends RecyclerView.LayoutParams { 762b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 763b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 764b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Span Id for Views that are not laid out yet. 765b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 766b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final int INVALID_SPAN_ID = -1; 767b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 768b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private int mSpanIndex = INVALID_SPAN_ID; 769b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 770b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private int mSpanSize = 0; 771b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 772b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(Context c, AttributeSet attrs) { 773b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(c, attrs); 774b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 775b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 776b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(int width, int height) { 777b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(width, height); 778b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 779b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 780b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(ViewGroup.MarginLayoutParams source) { 781b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 782b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 783b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 784b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(ViewGroup.LayoutParams source) { 785b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 786b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 787b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 788b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(RecyclerView.LayoutParams source) { 789b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 790b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 791b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 792b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 793b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the current span index of this View. If the View is not laid out yet, the return 794b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * value is <code>undefined</code>. 795b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 796b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Note that span index may change by whether the RecyclerView is RTL or not. For 797b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * example, if the number of spans is 3 and layout is RTL, the rightmost item will have 798b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * span index of 2. If the layout changes back to LTR, span index for this view will be 0. 799b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If the item was occupying 2 spans, span indices would be 1 and 0 respectively. 800b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 801b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If the View occupies multiple spans, span with the minimum index is returned. 802b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 803b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The span index of the View. 804b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 805b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex() { 806b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanIndex; 807b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 808b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 809b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 810b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of spans occupied by this View. If the View not laid out yet, the 811b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * return value is <code>undefined</code>. 812b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 813b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans occupied by this View. 814b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 815b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanSize() { 816b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanSize; 817b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 818b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 819b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 820b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar} 821