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 /** 41204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar * Span size have been changed but we've not done a new layout calculation. 42204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar */ 43204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar boolean mPendingSpanCountChange = false; 44b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int mSpanCount = DEFAULT_SPAN_COUNT; 45b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 467e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko * Right borders for each span. 477e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko * <p>For <b>i-th</b> item start is {@link #mCachedBorders}[i-1] + 1 487e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko * and end is {@link #mCachedBorders}[i]. 49b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 507e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko int [] mCachedBorders; 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 610194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta 620194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta /** 630194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * Constructor used when layout manager is set in XML by RecyclerView attribute 640194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * "layoutManager". If spanCount is not specified in the XML, it defaults to a 650194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * single column. 660194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * 670194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount 680194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta */ 690194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta public GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, 700194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta int defStyleRes) { 710194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta super(context, attrs, defStyleAttr, defStyleRes); 720194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes); 730194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta setSpanCount(properties.spanCount); 740194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta } 750194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta 765f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar /** 775f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * Creates a vertical GridLayoutManager 785f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * 795f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param context Current context, will be used to access resources. 805f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param spanCount The number of columns in the grid 815f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar */ 82b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public GridLayoutManager(Context context, int spanCount) { 83b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(context); 84b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar setSpanCount(spanCount); 85b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 86b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 875f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar /** 885f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param context Current context, will be used to access resources. 895f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param spanCount The number of columns or rows in the grid 905f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link 915f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * #VERTICAL}. 925f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * @param reverseLayout When set to true, layouts from end to start. 935f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar */ 94b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public GridLayoutManager(Context context, int spanCount, int orientation, 95b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar boolean reverseLayout) { 96b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(context, orientation, reverseLayout); 97b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar setSpanCount(spanCount); 98b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 99b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1005f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar /** 1015f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * stackFromEnd is not supported by GridLayoutManager. Consider using 1025f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar * {@link #setReverseLayout(boolean)}. 1035f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar */ 104b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 105061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void setStackFromEnd(boolean stackFromEnd) { 1065f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar if (stackFromEnd) { 1075f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar throw new UnsupportedOperationException( 1085f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar "GridLayoutManager does not support stack from end." 1095f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar + " Consider using reverse layout"); 1105f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar } 1115f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar super.setStackFromEnd(false); 112061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 113061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 114061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 115a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getRowCountForAccessibility(RecyclerView.Recycler recycler, 116a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state) { 117a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == HORIZONTAL) { 118a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanCount; 119a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 120a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (state.getItemCount() < 1) { 121a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 122a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 12356d2535547810327746f8306dbd1cf10a3f10870Steven Dao 12456d2535547810327746f8306dbd1cf10a3f10870Steven Dao // Row count is one more than the last item's row index. 12556d2535547810327746f8306dbd1cf10a3f10870Steven Dao return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1; 126a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 127a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 128a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 129a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, 130a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state) { 131a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == VERTICAL) { 132a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanCount; 133a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 134a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (state.getItemCount() < 1) { 135a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 136a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 13756d2535547810327746f8306dbd1cf10a3f10870Steven Dao 13856d2535547810327746f8306dbd1cf10a3f10870Steven Dao // Column count is one more than the last item's column index. 13956d2535547810327746f8306dbd1cf10a3f10870Steven Dao return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1; 140a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 141a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 142a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 143a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, 144a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { 145a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar ViewGroup.LayoutParams lp = host.getLayoutParams(); 146a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (!(lp instanceof LayoutParams)) { 147a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar super.onInitializeAccessibilityNodeInfoForItem(host, info); 148a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return; 149a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 150a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar LayoutParams glp = (LayoutParams) lp; 151115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition()); 152a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (mOrientation == HORIZONTAL) { 153a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( 154a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar glp.getSpanIndex(), glp.getSpanSize(), 155a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar spanGroupIndex, 1, 156a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); 157a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else { // VERTICAL 158a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( 159a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar spanGroupIndex , 1, 160a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar glp.getSpanIndex(), glp.getSpanSize(), 161a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); 162a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 163a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 164a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 165a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 166b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 1679ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (state.isPreLayout()) { 168061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar cachePreLayoutSpanMapping(); 1699ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 170b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super.onLayoutChildren(recycler, state); 171b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG) { 172b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar validateChildOrder(); 173b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 174061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar clearPreLayoutSpanMappingCache(); 1758cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar } 1768cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar 1778cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar @Override 1788cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar public void onLayoutCompleted(RecyclerView.State state) { 1798cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar super.onLayoutCompleted(state); 1808cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar mPendingSpanCountChange = false; 181061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 182061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 183061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private void clearPreLayoutSpanMappingCache() { 1849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar mPreLayoutSpanSizeCache.clear(); 185061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mPreLayoutSpanIndexCache.clear(); 1869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 1879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 188061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private void cachePreLayoutSpanMapping() { 1899ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int childCount = getChildCount(); 190061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < childCount; i++) { 1919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); 192115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final int viewPosition = lp.getViewLayoutPosition(); 193061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize()); 194061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex()); 1959ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 196b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 197b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 198b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 199061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { 200061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 201061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 202061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 203061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 204061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsChanged(RecyclerView recyclerView) { 205061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 206061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 207061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 208061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 209061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { 210061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 211061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 212061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 213061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 21421b345f101abc385496f42d250e580d21f1287b6Dake Gu public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, 21521b345f101abc385496f42d250e580d21f1287b6Dake Gu Object payload) { 216061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 217061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 218061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 219061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 220061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { 221061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 222061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 223061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 224061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar @Override 225b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public RecyclerView.LayoutParams generateDefaultLayoutParams() { 2264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == HORIZONTAL) { 2274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 2284143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ViewGroup.LayoutParams.FILL_PARENT); 2294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 2304143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 2314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ViewGroup.LayoutParams.WRAP_CONTENT); 2324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 233b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 234b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 235b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 236b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { 237b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams(c, attrs); 238b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 239b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 240b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 241b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 242b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (lp instanceof ViewGroup.MarginLayoutParams) { 243b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams((ViewGroup.MarginLayoutParams) lp); 244b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 245b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return new LayoutParams(lp); 246b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 247b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 248b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 249b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 250b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { 251b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return lp instanceof LayoutParams; 252b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 253b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 254b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 255b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Sets the source to get the number of spans occupied by each item in the adapter. 256b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 257b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans 258061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * occupied by each item 259b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 260b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) { 261b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSpanSizeLookup = spanSizeLookup; 262b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 263b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 264b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 265b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager. 266b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 267b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The current {@link SpanSizeLookup} used by the GridLayoutManager. 268b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 269b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public SpanSizeLookup getSpanSizeLookup() { 270b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanSizeLookup; 271b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 272b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 273b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private void updateMeasurements() { 274b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int totalSpace; 275b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (getOrientation() == VERTICAL) { 276b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); 277b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 278b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar totalSpace = getHeight() - getPaddingBottom() - getPaddingTop(); 279b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 2807e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko calculateItemBorders(totalSpace); 2817e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko } 2827e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko 2834143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar @Override 2844143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) { 2854143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mCachedBorders == null) { 2864143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar super.setMeasuredDimension(childrenBounds, wSpec, hSpec); 2874143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 2884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int width, height; 2896b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int horizontalPadding = getPaddingLeft() + getPaddingRight(); 2906b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int verticalPadding = getPaddingTop() + getPaddingBottom(); 2914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == VERTICAL) { 2926b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int usedHeight = childrenBounds.height() + verticalPadding; 2934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar height = chooseSize(hSpec, usedHeight, getMinimumHeight()); 2946b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding, 2954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getMinimumWidth()); 2964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 2976b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar final int usedWidth = childrenBounds.width() + horizontalPadding; 2984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar width = chooseSize(wSpec, usedWidth, getMinimumWidth()); 2996b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding, 3004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar getMinimumHeight()); 3017e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko } 3024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar setMeasuredDimension(width, height); 3034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 3044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 3054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar /** 3064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param totalSpace Total available space after padding is removed 3074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar */ 3084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private void calculateItemBorders(int totalSpace) { 3094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mCachedBorders = calculateItemBorders(mCachedBorders, mSpanCount, totalSpace); 3104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 3114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 3124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar /** 3134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param cachedBorders The out array 3144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param spanCount number of spans 3154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param totalSpace total available space after padding is removed 3164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @return The updated array. Might be the same instance as the provided array if its size 3174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * has not changed. 3184143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar */ 3194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) { 3204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (cachedBorders == null || cachedBorders.length != spanCount + 1 3214143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar || cachedBorders[cachedBorders.length - 1] != totalSpace) { 3224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar cachedBorders = new int[spanCount + 1]; 3234143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 3244143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar cachedBorders[0] = 0; 3254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int sizePerSpan = totalSpace / spanCount; 3264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar int sizePerSpanRemainder = totalSpace % spanCount; 3277e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko int consumedPixels = 0; 3287e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko int additionalSize = 0; 3294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (int i = 1; i <= spanCount; i++) { 3307e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko int itemSize = sizePerSpan; 3317e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko additionalSize += sizePerSpanRemainder; 3324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) { 3337e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko itemSize += 1; 3344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar additionalSize -= spanCount; 3357e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko } 3367e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko consumedPixels += itemSize; 3374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar cachedBorders[i] = consumedPixels; 3387e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko } 3394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar return cachedBorders; 340b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 341b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 342b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 343cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state, 344f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar AnchorInfo anchorInfo, int itemDirection) { 345f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar super.onAnchorReady(recycler, state, anchorInfo, itemDirection); 346b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar updateMeasurements(); 347061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (state.getItemCount() > 0 && !state.isPreLayout()) { 348f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection); 3499ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 350bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar ensureViewSet(); 351bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar } 352bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar 353bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar private void ensureViewSet() { 3549ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (mSet == null || mSet.length != mSpanCount) { 3559ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar mSet = new View[mSpanCount]; 3569ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3579ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3589ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 359bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar @Override 360bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 361bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar RecyclerView.State state) { 362bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar updateMeasurements(); 363bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar ensureViewSet(); 364bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar return super.scrollHorizontallyBy(dx, recycler, state); 365bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar } 366bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar 367bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar @Override 368bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 369bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar RecyclerView.State state) { 370bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar updateMeasurements(); 371bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar ensureViewSet(); 372bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar return super.scrollVerticallyBy(dy, recycler, state); 373bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar } 374bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar 375f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar private void ensureAnchorIsInCorrectSpan(RecyclerView.Recycler recycler, 376f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar RecyclerView.State state, AnchorInfo anchorInfo, int itemDirection) { 377f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar final boolean layingOutInPrimaryDirection = 378f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar itemDirection == LayoutState.ITEM_DIRECTION_TAIL; 379cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar int span = getSpanIndex(recycler, state, anchorInfo.mPosition); 380f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar if (layingOutInPrimaryDirection) { 381f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar // choose span 0 382f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar while (span > 0 && anchorInfo.mPosition > 0) { 383f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar anchorInfo.mPosition--; 384f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar span = getSpanIndex(recycler, state, anchorInfo.mPosition); 385f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 386f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } else { 387f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar // choose the max span we can get. hopefully last one 388f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar final int indexLimit = state.getItemCount() - 1; 389f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar int pos = anchorInfo.mPosition; 390f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar int bestSpan = span; 391f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar while (pos < indexLimit) { 392f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar int next = getSpanIndex(recycler, state, pos + 1); 393f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar if (next > bestSpan) { 394f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar pos += 1; 395f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar bestSpan = next; 396f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } else { 397f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar break; 398f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 399f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 400f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar anchorInfo.mPosition = pos; 401061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 402061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 403061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 4041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar @Override 405cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state, 406cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar int start, int end, int itemCount) { 4071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ensureLayoutState(); 4081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View invalidMatch = null; 4091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View outOfBoundsMatch = null; 4101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int boundsStart = mOrientationHelper.getStartAfterPadding(); 4111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int boundsEnd = mOrientationHelper.getEndAfterPadding(); 4121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int diff = end > start ? 1 : -1; 4131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = start; i != end; i += diff) { 4141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final View view = getChildAt(i); 4151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int position = getPosition(view); 4161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (position >= 0 && position < itemCount) { 417cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar final int span = getSpanIndex(recycler, state, position); 4181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (span != 0) { 4191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar continue; 4201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { 4221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (invalidMatch == null) { 4231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar invalidMatch = view; // removed item, least preferred 4241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd || 4261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mOrientationHelper.getDecoratedEnd(view) < boundsStart) { 4271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (outOfBoundsMatch == null) { 4281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar outOfBoundsMatch = view; // item is not visible, less preferred 4291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 4311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar return view; 4321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; 4361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 438a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state, 439a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int viewPosition) { 440a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (!state.isPreLayout()) { 441a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount); 442a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 443a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); 444a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (adapterPosition == -1) { 445a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (DEBUG) { 446a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar throw new RuntimeException("Cannot find span group index for position " 447a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar + viewPosition); 448a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 449a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); 450a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 451a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 452a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount); 453a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 454a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 455061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { 456061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!state.isPreLayout()) { 457061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount); 458061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 459061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int cached = mPreLayoutSpanIndexCache.get(pos, -1); 460061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (cached != -1) { 461061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return cached; 462061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 463061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); 464061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (adapterPosition == -1) { 465061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (DEBUG) { 466061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar throw new RuntimeException("Cannot find span index for pre layout position. It is" 467061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar + " not cached, not in the adapter. Pos:" + pos); 468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 469061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. It is" 470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar + " not cached, not in the adapter. Pos:" + pos); 471061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return 0; 472b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 473061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount); 4749ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 4759ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 4769ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { 4779ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (!state.isPreLayout()) { 4789ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mSpanSizeLookup.getSpanSize(pos); 479b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 4809ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int cached = mPreLayoutSpanSizeCache.get(pos, -1); 4819ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (cached != -1) { 4829ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return cached; 4839ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 4849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); 4859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (adapterPosition == -1) { 4869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (DEBUG) { 4879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar throw new RuntimeException("Cannot find span size for pre layout position. It is" 4889ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar + " not cached, not in the adapter. Pos:" + pos); 4899ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 4909ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. It is" 4919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar + " not cached, not in the adapter. Pos:" + pos); 4929ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return 1; 4939ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 4949ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mSpanSizeLookup.getSpanSize(adapterPosition); 495b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 496b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 497b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 498b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, 499b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutState layoutState, LayoutChunkResult result) { 5004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int otherDirSpecMode = mOrientationHelper.getModeInOther(); 5014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final boolean flexibleInOtherDir = otherDirSpecMode != View.MeasureSpec.EXACTLY; 5024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int currentOtherDirSize = getChildCount() > 0 ? mCachedBorders[mSpanCount] : 0; 5034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // if grid layout's dimensions are not specified, let the new row change the measurements 5044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // This is not perfect since we not covering all rows but still solves an important case 5054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // where they may have a header row which should be laid out according to children. 5064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (flexibleInOtherDir) { 5074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar updateMeasurements(); // reset measurements 5084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 509061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final boolean layingOutInPrimaryDirection = 510061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; 511b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int count = 0; 512061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int consumedSpanCount = 0; 513b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int remainingSpan = mSpanCount; 514061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!layingOutInPrimaryDirection) { 515061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); 516061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); 517061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar remainingSpan = itemSpanIndex + itemSpanSize; 518061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 519b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { 520b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int pos = layoutState.mCurrentPosition; 5219ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int spanSize = getSpanSize(recycler, state, pos); 52273304eff156157f62075215e795e774803a6f96aYigit Boyar if (spanSize > mSpanCount) { 52373304eff156157f62075215e795e774803a6f96aYigit Boyar throw new IllegalArgumentException("Item at position " + pos + " requires " + 52473304eff156157f62075215e795e774803a6f96aYigit Boyar spanSize + " spans but GridLayoutManager has only " + mSpanCount 52573304eff156157f62075215e795e774803a6f96aYigit Boyar + " spans."); 52673304eff156157f62075215e795e774803a6f96aYigit Boyar } 527b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar remainingSpan -= spanSize; 528b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (remainingSpan < 0) { 529b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar break; // item did not fit into this row or column 530b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 531b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = layoutState.next(recycler); 532b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (view == null) { 533b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar break; 534b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 535061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar consumedSpanCount += spanSize; 536b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSet[count] = view; 537061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar count++; 538b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 539b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 540b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (count == 0) { 541b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mFinished = true; 542b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 543b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 544b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 545b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int maxSize = 0; 5464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar float maxSizeInOther = 0; // use a float to get size per span 547061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 5485e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // we should assign spans before item decor offsets are calculated 549061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); 550061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < count; i++) { 551b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = mSet[i]; 5529ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layoutState.mScrapList == null) { 5539ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layingOutInPrimaryDirection) { 5549ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addView(view); 5559ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } else { 5569ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addView(view, 0); 5579ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 558b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 5599ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layingOutInPrimaryDirection) { 5609ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addDisappearingView(view); 5619ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } else { 5629ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addDisappearingView(view, 0); 5639ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 564b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 5659ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 566061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 5674143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int spec = getChildMeasureSpec(mCachedBorders[lp.mSpanIndex + lp.mSpanSize] - 5684143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mCachedBorders[lp.mSpanIndex], otherDirSpecMode, 0, 5694143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientation == HORIZONTAL ? lp.height : lp.width, 5704143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar false); 5714143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int mainSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), 5724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientationHelper.getMode(), 0, 5734143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientation == VERTICAL ? lp.height : lp.width, true); 57429f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar // Unless the child has MATCH_PARENT, measure it from its specs before adding insets. 575b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 57629f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar @SuppressWarnings("deprecation") 57729f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar final boolean applyInsets = lp.height == ViewGroup.LayoutParams.FILL_PARENT; 57829f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar measureChildWithDecorationsAndMargin(view, spec, mainSpec, applyInsets, false); 579b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 58029f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar //noinspection deprecation 58129f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar final boolean applyInsets = lp.width == ViewGroup.LayoutParams.FILL_PARENT; 58229f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar measureChildWithDecorationsAndMargin(view, mainSpec, spec, applyInsets, false); 583b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 584b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar final int size = mOrientationHelper.getDecoratedMeasurement(view); 585b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (size > maxSize) { 586b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar maxSize = size; 587b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 5884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view) / 5894143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar lp.mSpanSize; 5904143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (otherSize > maxSizeInOther) { 5914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSizeInOther = otherSize; 5924143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 593b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 5944143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (flexibleInOtherDir) { 5954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // re-distribute columns 5964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar guessMeasurement(maxSizeInOther, currentOtherDirSize); 5974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // now we should re-measure any item that was match parent. 5984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSize = 0; 5994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (int i = 0; i < count; i++) { 6004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar View view = mSet[i]; 6014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 6024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int spec = getChildMeasureSpec(mCachedBorders[lp.mSpanIndex + lp.mSpanSize] - 6034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mCachedBorders[lp.mSpanIndex], View.MeasureSpec.EXACTLY, 0, 6044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientation == HORIZONTAL ? lp.height : lp.width, false); 6054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int mainSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), 6064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientationHelper.getMode(), 0, 6074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientation == VERTICAL ? lp.height : lp.width, true); 6084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (mOrientation == VERTICAL) { 6094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measureChildWithDecorationsAndMargin(view, spec, mainSpec, false, true); 6104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 6114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measureChildWithDecorationsAndMargin(view, mainSpec, spec, false, true); 6124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int size = mOrientationHelper.getDecoratedMeasurement(view); 6144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (size > maxSize) { 6154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSize = size; 6164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6184143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // Views that did not measure the maxSize has to be re-measured 6204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // We will stop doing this once we introduce Gravity in the GLM layout params 6214143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int maxMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxSize, 6224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar View.MeasureSpec.EXACTLY); 623afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar for (int i = 0; i < count; i ++) { 624afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar final View view = mSet[i]; 625afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) { 6267e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 6274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int spec = getChildMeasureSpec(mCachedBorders[lp.mSpanIndex + lp.mSpanSize] 6284143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar - mCachedBorders[lp.mSpanIndex], View.MeasureSpec.EXACTLY, 0, 6294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar mOrientation == HORIZONTAL ? lp.height : lp.width, false); 630afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar if (mOrientation == VERTICAL) { 6314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec, true, true); 632afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } else { 6334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec, true, true); 634afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 635afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 636afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 637afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar 638b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mConsumed = maxSize; 639b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 6405e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar int left = 0, right = 0, top = 0, bottom = 0; 641b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 642b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 643b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = layoutState.mOffset; 644b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = bottom - maxSize; 645b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 646b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = layoutState.mOffset; 647b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + maxSize; 648b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 649b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 650b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 651b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = layoutState.mOffset; 652b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = right - maxSize; 653b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 654b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = layoutState.mOffset; 655b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = left + maxSize; 656b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 657b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 658061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < count; i++) { 659b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = mSet[i]; 660b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutParams params = (LayoutParams) view.getLayoutParams(); 661b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 6624143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (isLayoutRTL()) { 6634143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar right = getPaddingLeft() + mCachedBorders[params.mSpanIndex + params.mSpanSize]; 6644143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); 6654143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 6664143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar left = getPaddingLeft() + mCachedBorders[params.mSpanIndex]; 6674143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); 6684143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 669b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 6707e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko top = getPaddingTop() + mCachedBorders[params.mSpanIndex]; 671b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); 672b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 673b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // We calculate everything with View's bounding box (which includes decor and margins) 674b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // To calculate correct layout position, we subtract margins. 6755f538711872b050b93f49a5dcaff1753e0299449Dake Gu layoutDecoratedWithMargins(view, left, top, right, bottom); 676b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG) { 677b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" 678b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" 679b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin) 6805e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize); 681b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 682b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // Consume the available space if the view is not removed OR changed 683b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (params.isItemRemoved() || params.isItemChanged()) { 684b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mIgnoreConsumed = true; 685b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 686b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mFocusable |= view.isFocusable(); 687b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 688b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Arrays.fill(mSet, null); 689b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 690b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 6914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar /** 6924143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * This is called after laying out a row (if vertical) or a column (if horizontal) when the 6934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * RecyclerView does not have exact measurement specs. 6944143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * <p> 6954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * Here we try to assign a best guess width or height and re-do the layout to update other 6964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * views that wanted to FILL_PARENT in the non-scroll orientation. 6974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * 6984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param maxSizeInOther The maximum size per span ratio from the measurement of the children. 6994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param currentOtherDirSize The size before this layout chunk. There is no reason to go below. 7004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar */ 7014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private void guessMeasurement(float maxSizeInOther, int currentOtherDirSize) { 7024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int contentSize = Math.round(maxSizeInOther * mSpanCount); 7034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // always re-calculate because borders were stretched during the fill 7044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar calculateItemBorders(Math.max(contentSize, currentOtherDirSize)); 705061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 706061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 7073bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec, 7084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar boolean capBothSpecs, boolean alreadyMeasured) { 7095e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar calculateItemDecorationsForChild(child, mDecorInsets); 710b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); 7113bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar if (capBothSpecs || mOrientation == VERTICAL) { 7123bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left, 7133bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar lp.rightMargin + mDecorInsets.right); 7143bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar } 7153bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar if (capBothSpecs || mOrientation == HORIZONTAL) { 7163bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top, 7173bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar lp.bottomMargin + mDecorInsets.bottom); 7183bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar } 7194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final boolean measure; 7204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (alreadyMeasured) { 7214143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp); 7224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 7234143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measure = shouldMeasureChild(child, widthSpec, heightSpec, lp); 7244143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (measure) { 7264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar child.measure(widthSpec, heightSpec); 7274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7284143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 729b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 730b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 731b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private int updateSpecWithExtra(int spec, int startInset, int endInset) { 732b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (startInset == 0 && endInset == 0) { 733b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return spec; 734b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 735b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar final int mode = View.MeasureSpec.getMode(spec); 736b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) { 737b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return View.MeasureSpec.makeMeasureSpec( 73829f43aa1bb825a46fd7cf161a0cd1d5353aaf821Yigit Boyar Math.max(0, View.MeasureSpec.getSize(spec) - startInset - endInset), mode); 739b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 740b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return spec; 741b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 742b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 7439ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, 744061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int consumedSpanCount, boolean layingOutInPrimaryDirection) { 7455e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar int span, spanDiff, start, end, diff; 7465e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // make sure we traverse from min position to max position 7475e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (layingOutInPrimaryDirection) { 7485e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar start = 0; 7495e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar end = count; 7505e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar diff = 1; 7515e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 7525e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar start = count - 1; 7535e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar end = -1; 7545e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar diff = -1; 7555e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7565e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span 757871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar span = mSpanCount - 1; 7585e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar spanDiff = -1; 7595e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 7605e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar span = 0; 7615e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar spanDiff = 1; 7625e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7635e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar for (int i = start; i != end; i += diff) { 7645e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar View view = mSet[i]; 7655e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar LayoutParams params = (LayoutParams) view.getLayoutParams(); 7669ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); 7675e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (spanDiff == -1 && params.mSpanSize > 1) { 7685e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar params.mSpanIndex = span - (params.mSpanSize - 1); 7695e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 7705e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar params.mSpanIndex = span; 7715e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7725e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar span += spanDiff * params.mSpanSize; 7735e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7745e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7755e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar 776b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 777b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of spans laid out by this grid. 778b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 779b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans 780b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see #setSpanCount(int) 781b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 782b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanCount() { 783b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanCount; 784b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 785b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 786b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 787b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Sets the number of spans to be laid out. 788b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 789b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns. 790b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows. 791b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 792b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanCount The total number of spans in the grid 793b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see #getSpanCount() 794b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 795b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void setSpanCount(int spanCount) { 796b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (spanCount == mSpanCount) { 797b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 798b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 799204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar mPendingSpanCountChange = true; 800b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (spanCount < 1) { 801b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar throw new IllegalArgumentException("Span count should be at least 1. Provided " 802b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + spanCount); 803b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 804b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSpanCount = spanCount; 805061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 80666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar requestLayout(); 807b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 808b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 809b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 810b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * A helper class to provide the number of spans each item occupies. 811b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 812b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Default implementation sets each item to occupy exactly 1 span. 813b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 814b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup) 815b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 816b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static abstract class SpanSizeLookup { 817061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 818061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final SparseIntArray mSpanIndexCache = new SparseIntArray(); 819061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 820061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private boolean mCacheSpanIndices = false; 821061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 822b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 823b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of span occupied by the item at <code>position</code>. 824b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 825b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param position The adapter position of the item 826b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans occupied by the item at the provided position 827b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 828b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar abstract public int getSpanSize(int position); 829b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 830b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 831061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or 832061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * not. By default these values are not cached. If you are not overriding 833061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * {@link #getSpanIndex(int, int)}, you should set this to true for better performance. 834061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * 835061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not. 836061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 837061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) { 838061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mCacheSpanIndices = cacheSpanIndices; 839061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 840061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 841061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 842061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Clears the span index cache. GridLayoutManager automatically calls this method when 843061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * adapter changes occur. 844061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 845061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void invalidateSpanIndexCache() { 846061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanIndexCache.clear(); 847061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 848061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 849061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 850061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not. 851061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * 852061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @return True if results of {@link #getSpanIndex(int, int)} are cached. 853061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 854061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public boolean isSpanIndexCacheEnabled() { 855061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mCacheSpanIndices; 856061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 857061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 858061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int getCachedSpanIndex(int position, int spanCount) { 859061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!mCacheSpanIndices) { 860061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return getSpanIndex(position, spanCount); 861061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 862061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int existing = mSpanIndexCache.get(position, -1); 863061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (existing != -1) { 864061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return existing; 865061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 866061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int value = getSpanIndex(position, spanCount); 867061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanIndexCache.put(position, value); 868061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return value; 869061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 870061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 871061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 872b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the final span index of the provided position. 873b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 874061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * If you have a faster way to calculate span index for your items, you should override 875061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * this method. Otherwise, you should enable span index cache 876061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is 877061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * disabled, default implementation traverses all items from 0 to 878061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * <code>position</code>. When caching is enabled, it calculates from the closest cached 879061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * value before the <code>position</code>. 880061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * <p> 881061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * If you override this method, you need to make sure it is consistent with 882a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for 883061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * each item. It is called only for the reference item and rest of the items 884061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * are assigned to spans based on the reference item. For example, you cannot assign a 885061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * position to span 2 while span 1 is empty. 886b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 887061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Note that span offsets always start with 0 and are not affected by RTL. 888b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 889061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @param position The position of the item 890b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanCount The total number of spans in the grid 891b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The final span position of the item. Should be between 0 (inclusive) and 892b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <code>spanCount</code>(exclusive) 893b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 894b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex(int position, int spanCount) { 895b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int positionSpanSize = getSpanSize(position); 896b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (positionSpanSize == spanCount) { 897b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 0; // quick return for full-span items 898b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 89973304eff156157f62075215e795e774803a6f96aYigit Boyar int span = 0; 900061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int startPos = 0; 901061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar // If caching is enabled, try to jump 902061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (mCacheSpanIndices && mSpanIndexCache.size() > 0) { 903061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int prevKey = findReferenceIndexFromCache(position); 904061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (prevKey >= 0) { 905061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey); 906061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar startPos = prevKey + 1; 907061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 908061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 909061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = startPos; i < position; i++) { 91073304eff156157f62075215e795e774803a6f96aYigit Boyar int size = getSpanSize(i); 911b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span += size; 912b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (span == spanCount) { 913b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span = 0; 914b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else if (span > spanCount) { 915b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // did not fit, moving to next row / column 916b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span = size; 917b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 918b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 919b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (span + positionSpanSize <= spanCount) { 920b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return span; 921b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 922b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 0; 923b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 924061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 925061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int findReferenceIndexFromCache(int position) { 926061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int lo = 0; 927061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int hi = mSpanIndexCache.size() - 1; 928061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 929061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar while (lo <= hi) { 930061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int mid = (lo + hi) >>> 1; 931061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int midVal = mSpanIndexCache.keyAt(mid); 932061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (midVal < position) { 933061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar lo = mid + 1; 934061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } else { 935061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar hi = mid - 1; 936061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 937061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 938061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int index = lo - 1; 939061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (index >= 0 && index < mSpanIndexCache.size()) { 940061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanIndexCache.keyAt(index); 941061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 942061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return -1; 943061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 944a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 945a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar /** 946a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * Returns the index of the group this position belongs. 947a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * <p> 948a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * For example, if grid has 3 columns and each item occupies 1 span, span group index 949a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * for item 1 will be 0, item 5 will be 1. 950a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * 951a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @param adapterPosition The position in adapter 952a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @param spanCount The total number of spans in the grid 953a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @return The index of the span group including the item at the given adapter position 954a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar */ 955a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getSpanGroupIndex(int adapterPosition, int spanCount) { 956a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int span = 0; 957a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int group = 0; 958a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int positionSpanSize = getSpanSize(adapterPosition); 959a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar for (int i = 0; i < adapterPosition; i++) { 960a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int size = getSpanSize(i); 961a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span += size; 962a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (span == spanCount) { 963a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span = 0; 964a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 965a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else if (span > spanCount) { 966a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar // did not fit, moving to next row / column 967a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span = size; 968a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 969a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 970a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 971a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (span + positionSpanSize > spanCount) { 972a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 973a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 974a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return group; 975a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 976b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 977b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 978b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 979f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar public View onFocusSearchFailed(View focused, int focusDirection, 980f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state) { 981f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View prevFocusedChild = findContainingItemView(focused); 982f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (prevFocusedChild == null) { 983f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 984f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 985f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar LayoutParams lp = (LayoutParams) prevFocusedChild.getLayoutParams(); 986f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int prevSpanStart = lp.mSpanIndex; 987f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int prevSpanEnd = lp.mSpanIndex + lp.mSpanSize; 988f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View view = super.onFocusSearchFailed(focused, focusDirection, recycler, state); 989f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (view == null) { 990f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 991f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 992f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar // LinearLayoutManager finds the last child. What we want is the child which has the same 993f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar // spanIndex. 994f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection); 995f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final boolean ascend = (layoutDir == LayoutState.LAYOUT_END) != mShouldReverseLayout; 996f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int start, inc, limit; 997f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (ascend) { 998f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar start = getChildCount() - 1; 999f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar inc = -1; 1000f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar limit = -1; 1001f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 1002f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar start = 0; 1003f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar inc = 1; 1004f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar limit = getChildCount(); 1005f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1006f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final boolean preferLastSpan = mOrientation == VERTICAL && isLayoutRTL(); 1007f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View weakCandidate = null; // somewhat matches but not strong 1008f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int weakCandidateSpanIndex = -1; 1009f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int weakCandidateOverlap = 0; // how many spans overlap 1010f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 1011f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar for (int i = start; i != limit; i += inc) { 1012f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View candidate = getChildAt(i); 1013f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (candidate == prevFocusedChild) { 1014f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar break; 1015f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1016f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (!candidate.isFocusable()) { 1017f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar continue; 1018f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1019f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final LayoutParams candidateLp = (LayoutParams) candidate.getLayoutParams(); 1020f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int candidateStart = candidateLp.mSpanIndex; 1021f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int candidateEnd = candidateLp.mSpanIndex + candidateLp.mSpanSize; 1022f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (candidateStart == prevSpanStart && candidateEnd == prevSpanEnd) { 1023f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return candidate; // perfect match 1024f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1025f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar boolean assignAsWeek = false; 1026f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (weakCandidate == null) { 1027f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar assignAsWeek = true; 1028f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 1029f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int maxStart = Math.max(candidateStart, prevSpanStart); 1030f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int minEnd = Math.min(candidateEnd, prevSpanEnd); 1031f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int overlap = minEnd - maxStart; 1032f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (overlap > weakCandidateOverlap) { 1033f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar assignAsWeek = true; 1034f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else if (overlap == weakCandidateOverlap && 1035f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar preferLastSpan == (candidateStart > weakCandidateSpanIndex)) { 1036f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar assignAsWeek = true; 1037f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1038f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1039f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 1040f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (assignAsWeek) { 1041f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar weakCandidate = candidate; 1042f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar weakCandidateSpanIndex = candidateLp.mSpanIndex; 1043f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar weakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd) - 1044f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar Math.max(candidateStart, prevSpanStart); 1045f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1046f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1047f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return weakCandidate; 1048f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1049f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 1050f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @Override 1051b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean supportsPredictiveItemAnimations() { 1052204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar return mPendingSavedState == null && !mPendingSpanCountChange; 1053b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1054b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1055b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1056b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span. 1057b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1058b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final class DefaultSpanSizeLookup extends SpanSizeLookup { 1059061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 1060b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 1061b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanSize(int position) { 1062b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 1; 1063b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1064b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1065b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 1066b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex(int position, int spanCount) { 1067b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return position % spanCount; 1068b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1069b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1070b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1071b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1072b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * LayoutParams used by GridLayoutManager. 107342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * <p> 107442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the 107542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is 107642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * expected to fill all of the space given to it. 1077b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1078b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static class LayoutParams extends RecyclerView.LayoutParams { 1079b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1080b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1081b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Span Id for Views that are not laid out yet. 1082b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1083b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final int INVALID_SPAN_ID = -1; 1084b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1085b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private int mSpanIndex = INVALID_SPAN_ID; 1086b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1087b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar private int mSpanSize = 0; 1088b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1089b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(Context c, AttributeSet attrs) { 1090b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(c, attrs); 1091b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1092b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1093b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(int width, int height) { 1094b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(width, height); 1095b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1096b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1097b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(ViewGroup.MarginLayoutParams source) { 1098b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 1099b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1100b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1101b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(ViewGroup.LayoutParams source) { 1102b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 1103b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1104b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1105b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(RecyclerView.LayoutParams source) { 1106b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 1107b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1108b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1109b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1110b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the current span index of this View. If the View is not laid out yet, the return 1111b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * value is <code>undefined</code>. 1112b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 1113b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Note that span index may change by whether the RecyclerView is RTL or not. For 1114b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * example, if the number of spans is 3 and layout is RTL, the rightmost item will have 1115b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * span index of 2. If the layout changes back to LTR, span index for this view will be 0. 1116b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If the item was occupying 2 spans, span indices would be 1 and 0 respectively. 1117b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 1118b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If the View occupies multiple spans, span with the minimum index is returned. 1119b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 1120b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The span index of the View. 1121b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1122b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex() { 1123b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanIndex; 1124b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1125b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1126b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1127b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of spans occupied by this View. If the View not laid out yet, the 1128b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * return value is <code>undefined</code>. 1129b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 1130b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans occupied by this View. 1131b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1132b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanSize() { 1133b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanSize; 1134b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1135b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1136b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1137b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar} 1138