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. 13c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * See the License for the specific language 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, 228e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas ViewGroup.LayoutParams.MATCH_PARENT); 2294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 230e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas return new LayoutParams(ViewGroup.LayoutParams.MATCH_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 34295017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar int getSpaceForSpanRange(int startSpan, int spanSize) { 34395017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar if (mOrientation == VERTICAL && isLayoutRTL()) { 34495017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar return mCachedBorders[mSpanCount - startSpan] 34595017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar - mCachedBorders[mSpanCount - startSpan - spanSize]; 34695017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar } else { 34795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar return mCachedBorders[startSpan + spanSize] - mCachedBorders[startSpan]; 34895017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar } 34995017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar } 35095017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar 351b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 352cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state, 353f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar AnchorInfo anchorInfo, int itemDirection) { 354f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar super.onAnchorReady(recycler, state, anchorInfo, itemDirection); 355b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar updateMeasurements(); 356061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (state.getItemCount() > 0 && !state.isPreLayout()) { 357f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection); 3589ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 359bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar ensureViewSet(); 360bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar } 361bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar 362bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar private void ensureViewSet() { 3639ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (mSet == null || mSet.length != mSpanCount) { 3649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar mSet = new View[mSpanCount]; 3659ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3669ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 3679ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 368bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar @Override 369bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 370bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar RecyclerView.State state) { 371bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar updateMeasurements(); 372bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar ensureViewSet(); 373bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar return super.scrollHorizontallyBy(dx, recycler, state); 374bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar } 375bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar 376bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar @Override 377bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 378bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar RecyclerView.State state) { 379bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar updateMeasurements(); 380bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar ensureViewSet(); 381bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar return super.scrollVerticallyBy(dy, recycler, state); 382bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar } 383bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar 384f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar private void ensureAnchorIsInCorrectSpan(RecyclerView.Recycler recycler, 385f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar RecyclerView.State state, AnchorInfo anchorInfo, int itemDirection) { 386f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar final boolean layingOutInPrimaryDirection = 387f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar itemDirection == LayoutState.ITEM_DIRECTION_TAIL; 388cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar int span = getSpanIndex(recycler, state, anchorInfo.mPosition); 389f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar if (layingOutInPrimaryDirection) { 390f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar // choose span 0 391f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar while (span > 0 && anchorInfo.mPosition > 0) { 392f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar anchorInfo.mPosition--; 393f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar span = getSpanIndex(recycler, state, anchorInfo.mPosition); 394f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 395f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } else { 396f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar // choose the max span we can get. hopefully last one 397f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar final int indexLimit = state.getItemCount() - 1; 398f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar int pos = anchorInfo.mPosition; 399f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar int bestSpan = span; 400f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar while (pos < indexLimit) { 401f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar int next = getSpanIndex(recycler, state, pos + 1); 402f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar if (next > bestSpan) { 403f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar pos += 1; 404f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar bestSpan = next; 405f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } else { 406f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar break; 407f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 408f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar } 409f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar anchorInfo.mPosition = pos; 410061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 411061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 412061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 4131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar @Override 414cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state, 415cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar int start, int end, int itemCount) { 4161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ensureLayoutState(); 4171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View invalidMatch = null; 4181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View outOfBoundsMatch = null; 4191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int boundsStart = mOrientationHelper.getStartAfterPadding(); 4201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int boundsEnd = mOrientationHelper.getEndAfterPadding(); 4211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int diff = end > start ? 1 : -1; 42295017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar 4231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = start; i != end; i += diff) { 4241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final View view = getChildAt(i); 4251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int position = getPosition(view); 4261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (position >= 0 && position < itemCount) { 427cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar final int span = getSpanIndex(recycler, state, position); 4281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (span != 0) { 4291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar continue; 4301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { 4321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (invalidMatch == null) { 4331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar invalidMatch = view; // removed item, least preferred 4341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4351e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd 4361e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas || mOrientationHelper.getDecoratedEnd(view) < boundsStart) { 4371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (outOfBoundsMatch == null) { 4381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar outOfBoundsMatch = view; // item is not visible, less preferred 4391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 4411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar return view; 4421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; 4461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 4471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 448a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state, 449a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int viewPosition) { 450a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (!state.isPreLayout()) { 451a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount); 452a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 453a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); 454a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (adapterPosition == -1) { 455a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (DEBUG) { 456a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar throw new RuntimeException("Cannot find span group index for position " 457a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar + viewPosition); 458a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 459a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); 460a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return 0; 461a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 462a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount); 463a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 464a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 465061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { 466061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!state.isPreLayout()) { 467061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount); 468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 469061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int cached = mPreLayoutSpanIndexCache.get(pos, -1); 470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (cached != -1) { 471061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return cached; 472061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 473061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); 474061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (adapterPosition == -1) { 475061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (DEBUG) { 476061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar throw new RuntimeException("Cannot find span index for pre layout position. It is" 477061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar + " not cached, not in the adapter. Pos:" + pos); 478061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 479061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. It is" 480061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar + " not cached, not in the adapter. Pos:" + pos); 481061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return 0; 482b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 483061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount); 4849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 4859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 4869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { 4879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (!state.isPreLayout()) { 4889ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mSpanSizeLookup.getSpanSize(pos); 489b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 4909ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int cached = mPreLayoutSpanSizeCache.get(pos, -1); 4919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (cached != -1) { 4929ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return cached; 4939ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 4949ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); 4959ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (adapterPosition == -1) { 4969ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (DEBUG) { 4979ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar throw new RuntimeException("Cannot find span size for pre layout position. It is" 4989ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar + " not cached, not in the adapter. Pos:" + pos); 4999ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 5009ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar Log.w(TAG, "Cannot find span size for pre layout position. It is" 5019ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar + " not cached, not in the adapter. Pos:" + pos); 5029ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return 1; 5039ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 5049ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar return mSpanSizeLookup.getSpanSize(adapterPosition); 505b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 506b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 507b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 508945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState, 5093104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistry layoutPrefetchRegistry) { 510ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik int remainingSpan = mSpanCount; 511ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik int count = 0; 512ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { 513ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik final int pos = layoutState.mCurrentPosition; 5146425bbc0816bc5c2dbd14010d8dee4245d14fd1dChris Craik layoutPrefetchRegistry.addPosition(pos, Math.max(0, layoutState.mScrollingOffset)); 515ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik final int spanSize = mSpanSizeLookup.getSpanSize(pos); 516ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik remainingSpan -= spanSize; 517ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik layoutState.mCurrentPosition += layoutState.mItemDirection; 518ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik count++; 519ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 520ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik } 521ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik 522ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik @Override 523b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, 524b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutState layoutState, LayoutChunkResult result) { 5254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int otherDirSpecMode = mOrientationHelper.getModeInOther(); 5264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final boolean flexibleInOtherDir = otherDirSpecMode != View.MeasureSpec.EXACTLY; 5274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int currentOtherDirSize = getChildCount() > 0 ? mCachedBorders[mSpanCount] : 0; 5284143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // if grid layout's dimensions are not specified, let the new row change the measurements 5294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // This is not perfect since we not covering all rows but still solves an important case 5304143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // where they may have a header row which should be laid out according to children. 5314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (flexibleInOtherDir) { 5324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar updateMeasurements(); // reset measurements 5334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 534061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final boolean layingOutInPrimaryDirection = 535061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; 536b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int count = 0; 537061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int consumedSpanCount = 0; 538b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int remainingSpan = mSpanCount; 539061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!layingOutInPrimaryDirection) { 540061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); 541061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); 542061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar remainingSpan = itemSpanIndex + itemSpanSize; 543061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 544b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { 545b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int pos = layoutState.mCurrentPosition; 5469ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar final int spanSize = getSpanSize(recycler, state, pos); 54773304eff156157f62075215e795e774803a6f96aYigit Boyar if (spanSize > mSpanCount) { 5481e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas throw new IllegalArgumentException("Item at position " + pos + " requires " 5491e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas + spanSize + " spans but GridLayoutManager has only " + mSpanCount 55073304eff156157f62075215e795e774803a6f96aYigit Boyar + " spans."); 55173304eff156157f62075215e795e774803a6f96aYigit Boyar } 552b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar remainingSpan -= spanSize; 553b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (remainingSpan < 0) { 554b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar break; // item did not fit into this row or column 555b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 556b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = layoutState.next(recycler); 557b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (view == null) { 558b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar break; 559b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 560061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar consumedSpanCount += spanSize; 561b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSet[count] = view; 562061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar count++; 563b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 564b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 565b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (count == 0) { 566b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mFinished = true; 567b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 568b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 569b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 570b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int maxSize = 0; 5714143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar float maxSizeInOther = 0; // use a float to get size per span 572061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 5735e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // we should assign spans before item decor offsets are calculated 574061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); 575061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < count; i++) { 576b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = mSet[i]; 5779ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layoutState.mScrapList == null) { 5789ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layingOutInPrimaryDirection) { 5799ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addView(view); 5809ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } else { 5819ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addView(view, 0); 5829ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 583b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 5849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar if (layingOutInPrimaryDirection) { 5859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addDisappearingView(view); 5869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } else { 5879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar addDisappearingView(view, 0); 5889ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar } 589b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 590de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar calculateItemDecorationsForChild(view, mDecorInsets); 5919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar 592de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar measureChild(view, otherDirSpecMode, false); 593b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar final int size = mOrientationHelper.getDecoratedMeasurement(view); 594b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (size > maxSize) { 595b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar maxSize = size; 596b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 597de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 5981e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view) 5991e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas / lp.mSpanSize; 6004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (otherSize > maxSizeInOther) { 6014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSizeInOther = otherSize; 6024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 603b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 6044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (flexibleInOtherDir) { 6054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // re-distribute columns 6064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar guessMeasurement(maxSizeInOther, currentOtherDirSize); 6074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // now we should re-measure any item that was match parent. 6084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSize = 0; 6094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar for (int i = 0; i < count; i++) { 6104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar View view = mSet[i]; 611de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar measureChild(view, View.MeasureSpec.EXACTLY, true); 6124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int size = mOrientationHelper.getDecoratedMeasurement(view); 6134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (size > maxSize) { 6144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar maxSize = size; 6154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 6174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 618de8a5de43b647f9b61f4859a22a0ce59155f442aYigit 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 6211e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas for (int i = 0; i < count; i++) { 622afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar final View view = mSet[i]; 623afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) { 6247e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 625de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final Rect decorInsets = lp.mDecorInsets; 626de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int verticalInsets = decorInsets.top + decorInsets.bottom 627de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar + lp.topMargin + lp.bottomMargin; 628de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int horizontalInsets = decorInsets.left + decorInsets.right 629de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar + lp.leftMargin + lp.rightMargin; 63095017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar final int totalSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize); 631de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int wSpec; 632de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int hSpec; 633afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar if (mOrientation == VERTICAL) { 634de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar wSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY, 635de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar horizontalInsets, lp.width, false); 636de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar hSpec = View.MeasureSpec.makeMeasureSpec(maxSize - verticalInsets, 637de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar View.MeasureSpec.EXACTLY); 638afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } else { 639de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar wSpec = View.MeasureSpec.makeMeasureSpec(maxSize - horizontalInsets, 640de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar View.MeasureSpec.EXACTLY); 641de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar hSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY, 642de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar verticalInsets, lp.height, false); 643afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 644de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar measureChildWithDecorationsAndMargin(view, wSpec, hSpec, true); 645afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 646afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar } 647afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar 648b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mConsumed = maxSize; 649b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 6505e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar int left = 0, right = 0, top = 0, bottom = 0; 651b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 652b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 653b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = layoutState.mOffset; 654b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = bottom - maxSize; 655b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 656b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar top = layoutState.mOffset; 657b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + maxSize; 658b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 659b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 660b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { 661b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = layoutState.mOffset; 662b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = right - maxSize; 663b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 664b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar left = layoutState.mOffset; 665b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar right = left + maxSize; 666b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 667b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 668061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = 0; i < count; i++) { 669b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar View view = mSet[i]; 670b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar LayoutParams params = (LayoutParams) view.getLayoutParams(); 671b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (mOrientation == VERTICAL) { 6724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (isLayoutRTL()) { 67395017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar right = getPaddingLeft() + mCachedBorders[mSpanCount - params.mSpanIndex]; 6744143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); 6754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 6764143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar left = getPaddingLeft() + mCachedBorders[params.mSpanIndex]; 6774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); 6784143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 679b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else { 6807e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko top = getPaddingTop() + mCachedBorders[params.mSpanIndex]; 681b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); 682b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 683b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // We calculate everything with View's bounding box (which includes decor and margins) 684b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // To calculate correct layout position, we subtract margins. 6855f538711872b050b93f49a5dcaff1753e0299449Dake Gu layoutDecoratedWithMargins(view, left, top, right, bottom); 686b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (DEBUG) { 687b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" 688b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" 689b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin) 6905e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize); 691b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 692b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // Consume the available space if the view is not removed OR changed 693b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (params.isItemRemoved() || params.isItemChanged()) { 694b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar result.mIgnoreConsumed = true; 695b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 696b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri result.mFocusable |= view.hasFocusable(); 697b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 698b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar Arrays.fill(mSet, null); 699b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 700b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 7014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar /** 702de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * Measures a child with currently known information. This is not necessarily the child's final 703de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * measurement. (see fillChunk for details). 704de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * 705de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * @param view The child view to be measured 706de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * @param otherDirParentSpecMode The RV measure spec that should be used in the secondary 707de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * orientation 708de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar * @param alreadyMeasured True if we've already measured this view once 709de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar */ 710de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar private void measureChild(View view, int otherDirParentSpecMode, boolean alreadyMeasured) { 711de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 712de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final Rect decorInsets = lp.mDecorInsets; 713de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int verticalInsets = decorInsets.top + decorInsets.bottom 714de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar + lp.topMargin + lp.bottomMargin; 715de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int horizontalInsets = decorInsets.left + decorInsets.right 716de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar + lp.leftMargin + lp.rightMargin; 71795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar final int availableSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize); 718de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int wSpec; 719de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar final int hSpec; 720de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar if (mOrientation == VERTICAL) { 721de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, 722de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar horizontalInsets, lp.width, false); 723de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(), 724de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar verticalInsets, lp.height, true); 725de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar } else { 726de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, 727de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar verticalInsets, lp.height, false); 728de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(), 729de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar horizontalInsets, lp.width, true); 730de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar } 731de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar measureChildWithDecorationsAndMargin(view, wSpec, hSpec, alreadyMeasured); 732de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar } 733de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar 734de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar /** 7354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * This is called after laying out a row (if vertical) or a column (if horizontal) when the 7364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * RecyclerView does not have exact measurement specs. 7374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * <p> 7384143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * Here we try to assign a best guess width or height and re-do the layout to update other 739e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas * views that wanted to MATCH_PARENT in the non-scroll orientation. 7404143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * 7414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param maxSizeInOther The maximum size per span ratio from the measurement of the children. 7424143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar * @param currentOtherDirSize The size before this layout chunk. There is no reason to go below. 7434143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar */ 7444143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar private void guessMeasurement(float maxSizeInOther, int currentOtherDirSize) { 7454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final int contentSize = Math.round(maxSizeInOther * mSpanCount); 7464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar // always re-calculate because borders were stretched during the fill 7474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar calculateItemBorders(Math.max(contentSize, currentOtherDirSize)); 748061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 749061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 7503bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec, 751de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar boolean alreadyMeasured) { 752b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); 7534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar final boolean measure; 7544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (alreadyMeasured) { 7554143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp); 7564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } else { 7574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar measure = shouldMeasureChild(child, widthSpec, heightSpec, lp); 7584143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 7594143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar if (measure) { 7604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar child.measure(widthSpec, heightSpec); 7614143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 762b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 763b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 7649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, 765061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int consumedSpanCount, boolean layingOutInPrimaryDirection) { 76695017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar // spans are always assigned from 0 to N no matter if it is RTL or not. 76795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar // RTL is used only when positioning the view. 76895017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar int span, start, end, diff; 7695e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar // make sure we traverse from min position to max position 7705e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar if (layingOutInPrimaryDirection) { 7715e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar start = 0; 7725e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar end = count; 7735e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar diff = 1; 7745e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } else { 7755e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar start = count - 1; 7765e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar end = -1; 7775e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar diff = -1; 7785e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 77995017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar span = 0; 7805e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar for (int i = start; i != end; i += diff) { 7815e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar View view = mSet[i]; 7825e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar LayoutParams params = (LayoutParams) view.getLayoutParams(); 7839ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); 78495017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar params.mSpanIndex = span; 78595017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar span += params.mSpanSize; 7865e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7875e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar } 7885e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar 789b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 790b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of spans laid out by this grid. 791b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 792b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans 793b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see #setSpanCount(int) 794b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 795b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanCount() { 796b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanCount; 797b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 798b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 799b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 800b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Sets the number of spans to be laid out. 801b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 802b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns. 803b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows. 804b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 805b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanCount The total number of spans in the grid 806b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see #getSpanCount() 807b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 808b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public void setSpanCount(int spanCount) { 809b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (spanCount == mSpanCount) { 810b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return; 811b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 812204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar mPendingSpanCountChange = true; 813b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (spanCount < 1) { 814b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar throw new IllegalArgumentException("Span count should be at least 1. Provided " 815b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar + spanCount); 816b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 817b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar mSpanCount = spanCount; 818061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanSizeLookup.invalidateSpanIndexCache(); 81966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar requestLayout(); 820b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 821b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 822b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 823b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * A helper class to provide the number of spans each item occupies. 824b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 825b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Default implementation sets each item to occupy exactly 1 span. 826b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 827b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup) 828b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 8291e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public abstract static class SpanSizeLookup { 830061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 831061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final SparseIntArray mSpanIndexCache = new SparseIntArray(); 832061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 833061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar private boolean mCacheSpanIndices = false; 834061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 835b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 836b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of span occupied by the item at <code>position</code>. 837b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 838b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param position The adapter position of the item 839b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans occupied by the item at the provided position 840b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 8411e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas public abstract int getSpanSize(int position); 842b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 843b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 844061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or 845061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * not. By default these values are not cached. If you are not overriding 846061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * {@link #getSpanIndex(int, int)}, you should set this to true for better performance. 847061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * 848061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not. 849061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 850061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) { 851061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mCacheSpanIndices = cacheSpanIndices; 852061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 853061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 854061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 855061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Clears the span index cache. GridLayoutManager automatically calls this method when 856061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * adapter changes occur. 857061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 858061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public void invalidateSpanIndexCache() { 859061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanIndexCache.clear(); 860061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 861061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 862061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 863061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not. 864061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * 865061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @return True if results of {@link #getSpanIndex(int, int)} are cached. 866061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar */ 867061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar public boolean isSpanIndexCacheEnabled() { 868061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mCacheSpanIndices; 869061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 870061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 871061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int getCachedSpanIndex(int position, int spanCount) { 872061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (!mCacheSpanIndices) { 873061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return getSpanIndex(position, spanCount); 874061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 875061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int existing = mSpanIndexCache.get(position, -1); 876061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (existing != -1) { 877061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return existing; 878061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 879061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int value = getSpanIndex(position, spanCount); 880061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar mSpanIndexCache.put(position, value); 881061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return value; 882061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 883061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 884061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar /** 885b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the final span index of the provided position. 886b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 887061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * If you have a faster way to calculate span index for your items, you should override 888061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * this method. Otherwise, you should enable span index cache 889061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is 890061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * disabled, default implementation traverses all items from 0 to 891061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * <code>position</code>. When caching is enabled, it calculates from the closest cached 892061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * value before the <code>position</code>. 893061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * <p> 894061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * If you override this method, you need to make sure it is consistent with 895a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for 896061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * each item. It is called only for the reference item and rest of the items 897061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * are assigned to spans based on the reference item. For example, you cannot assign a 898061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * position to span 2 while span 1 is empty. 899b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 900061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * Note that span offsets always start with 0 and are not affected by RTL. 901b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 902061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar * @param position The position of the item 903b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @param spanCount The total number of spans in the grid 904b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The final span position of the item. Should be between 0 (inclusive) and 905b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <code>spanCount</code>(exclusive) 906b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 907b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex(int position, int spanCount) { 908b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar int positionSpanSize = getSpanSize(position); 909b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (positionSpanSize == spanCount) { 910b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 0; // quick return for full-span items 911b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 91273304eff156157f62075215e795e774803a6f96aYigit Boyar int span = 0; 913061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int startPos = 0; 914061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar // If caching is enabled, try to jump 915061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (mCacheSpanIndices && mSpanIndexCache.size() > 0) { 916061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int prevKey = findReferenceIndexFromCache(position); 917061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (prevKey >= 0) { 918061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey); 919061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar startPos = prevKey + 1; 920061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 921061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 922061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar for (int i = startPos; i < position; i++) { 92373304eff156157f62075215e795e774803a6f96aYigit Boyar int size = getSpanSize(i); 924b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span += size; 925b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (span == spanCount) { 926b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span = 0; 927b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } else if (span > spanCount) { 928b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar // did not fit, moving to next row / column 929b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar span = size; 930b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 931b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 932b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar if (span + positionSpanSize <= spanCount) { 933b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return span; 934b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 935b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 0; 936b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 937061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 938061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int findReferenceIndexFromCache(int position) { 939061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int lo = 0; 940061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int hi = mSpanIndexCache.size() - 1; 941061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 942061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar while (lo <= hi) { 943061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int mid = (lo + hi) >>> 1; 944061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar final int midVal = mSpanIndexCache.keyAt(mid); 945061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (midVal < position) { 946061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar lo = mid + 1; 947061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } else { 948061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar hi = mid - 1; 949061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 950061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 951061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar int index = lo - 1; 952061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar if (index >= 0 && index < mSpanIndexCache.size()) { 953061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return mSpanIndexCache.keyAt(index); 954061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 955061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar return -1; 956061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar } 957a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 958a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar /** 959a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * Returns the index of the group this position belongs. 960a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * <p> 961a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * For example, if grid has 3 columns and each item occupies 1 span, span group index 962a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * for item 1 will be 0, item 5 will be 1. 963a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * 964a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @param adapterPosition The position in adapter 965a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @param spanCount The total number of spans in the grid 966a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar * @return The index of the span group including the item at the given adapter position 967a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar */ 968a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public int getSpanGroupIndex(int adapterPosition, int spanCount) { 969a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int span = 0; 970a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int group = 0; 971a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int positionSpanSize = getSpanSize(adapterPosition); 972a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar for (int i = 0; i < adapterPosition; i++) { 973a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar int size = getSpanSize(i); 974a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span += size; 975a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (span == spanCount) { 976a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span = 0; 977a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 978a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } else if (span > spanCount) { 979a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar // did not fit, moving to next row / column 980a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar span = size; 981a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 982a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 983a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 984a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar if (span + positionSpanSize > spanCount) { 985a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar group++; 986a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 987a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar return group; 988a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 989b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 990b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 991b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 992f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar public View onFocusSearchFailed(View focused, int focusDirection, 993f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar RecyclerView.Recycler recycler, RecyclerView.State state) { 994f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View prevFocusedChild = findContainingItemView(focused); 995f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (prevFocusedChild == null) { 996f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 997f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 998f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar LayoutParams lp = (LayoutParams) prevFocusedChild.getLayoutParams(); 999f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int prevSpanStart = lp.mSpanIndex; 1000f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int prevSpanEnd = lp.mSpanIndex + lp.mSpanSize; 1001f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View view = super.onFocusSearchFailed(focused, focusDirection, recycler, state); 1002f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (view == null) { 1003f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return null; 1004f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1005f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar // LinearLayoutManager finds the last child. What we want is the child which has the same 1006f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar // spanIndex. 1007f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection); 1008f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final boolean ascend = (layoutDir == LayoutState.LAYOUT_END) != mShouldReverseLayout; 1009f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int start, inc, limit; 1010f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (ascend) { 1011f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar start = getChildCount() - 1; 1012f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar inc = -1; 1013f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar limit = -1; 1014f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 1015f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar start = 0; 1016f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar inc = 1; 1017f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar limit = getChildCount(); 1018f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1019f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final boolean preferLastSpan = mOrientation == VERTICAL && isLayoutRTL(); 1020f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 10219c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // The focusable candidate to be picked if no perfect focusable candidate is found. 10229c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // The best focusable candidate is the one with the highest amount of span overlap with 10239c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // the currently focused view. 10249c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri View focusableWeakCandidate = null; // somewhat matches but not strong 10259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int focusableWeakCandidateSpanIndex = -1; 10269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int focusableWeakCandidateOverlap = 0; // how many spans overlap 10279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 10289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // The unfocusable candidate to become visible on the screen next, if no perfect or 10299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // weak focusable candidates are found to receive focus next. 10309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // We are only interested in partially visible unfocusable views. These are views that are 10319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // not fully visible, that is either partially overlapping, or out-of-bounds and right below 10329c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // or above RV's padded bounded area. The best unfocusable candidate is the one with the 10339c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // highest amount of span overlap with the currently focused view. 10349c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri View unfocusableWeakCandidate = null; // somewhat matches but not strong 10359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int unfocusableWeakCandidateSpanIndex = -1; 10369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int unfocusableWeakCandidateOverlap = 0; // how many spans overlap 10379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 10389c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // The span group index of the start child. This indicates the span group index of the 10399c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // next focusable item to receive focus, if a focusable item within the same span group 10409c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // exists. Any focusable item beyond this group index are not relevant since they 10419c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // were already stored in the layout before onFocusSearchFailed call and were not picked 10429c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // by the focusSearch algorithm. 10439c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int focusableSpanGroupIndex = getSpanGroupIndex(recycler, state, start); 1044f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar for (int i = start; i != limit; i += inc) { 10459c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri int spanGroupIndex = getSpanGroupIndex(recycler, state, i); 1046f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View candidate = getChildAt(i); 1047f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (candidate == prevFocusedChild) { 1048f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar break; 1049f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 10509c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 1051b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (candidate.hasFocusable() && spanGroupIndex != focusableSpanGroupIndex) { 10529c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // We are past the allowable span group index for the next focusable item. 10539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // The search only continues if no focusable weak candidates have been found up 10549c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // until this point, in order to find the best unfocusable candidate to become 10559c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri // visible on the screen next. 10569c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (focusableWeakCandidate != null) { 10579c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri break; 10589c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 1059f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar continue; 1060f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 10619c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 1062f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final LayoutParams candidateLp = (LayoutParams) candidate.getLayoutParams(); 1063f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int candidateStart = candidateLp.mSpanIndex; 1064f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar final int candidateEnd = candidateLp.mSpanIndex + candidateLp.mSpanSize; 1065b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (candidate.hasFocusable() && candidateStart == prevSpanStart 10669c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri && candidateEnd == prevSpanEnd) { 1067f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar return candidate; // perfect match 1068f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1069f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar boolean assignAsWeek = false; 1070b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if ((candidate.hasFocusable() && focusableWeakCandidate == null) 1071b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri || (!candidate.hasFocusable() && unfocusableWeakCandidate == null)) { 1072f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar assignAsWeek = true; 1073f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } else { 1074f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int maxStart = Math.max(candidateStart, prevSpanStart); 1075f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int minEnd = Math.min(candidateEnd, prevSpanEnd); 1076f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar int overlap = minEnd - maxStart; 1077b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (candidate.hasFocusable()) { 10789c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (overlap > focusableWeakCandidateOverlap) { 10799c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri assignAsWeek = true; 10809c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else if (overlap == focusableWeakCandidateOverlap 10819c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri && preferLastSpan == (candidateStart 10829c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri > focusableWeakCandidateSpanIndex)) { 10839c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri assignAsWeek = true; 10849c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 10859c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else if (focusableWeakCandidate == null 10869c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri && isViewPartiallyVisible(candidate, false, true)) { 10879c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (overlap > unfocusableWeakCandidateOverlap) { 10889c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri assignAsWeek = true; 10899c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else if (overlap == unfocusableWeakCandidateOverlap 10909c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri && preferLastSpan == (candidateStart 10919c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri > unfocusableWeakCandidateSpanIndex)) { 10929c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri assignAsWeek = true; 10939c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 1094f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1095f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1096f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 1097f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (assignAsWeek) { 1098b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri if (candidate.hasFocusable()) { 10999c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri focusableWeakCandidate = candidate; 11009c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri focusableWeakCandidateSpanIndex = candidateLp.mSpanIndex; 11019c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri focusableWeakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd) 11029c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri - Math.max(candidateStart, prevSpanStart); 11039c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } else { 11049c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri unfocusableWeakCandidate = candidate; 11059c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri unfocusableWeakCandidateSpanIndex = candidateLp.mSpanIndex; 11069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri unfocusableWeakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd) 11079c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri - Math.max(candidateStart, prevSpanStart); 11089c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 1109f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1110f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 11119c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return (focusableWeakCandidate != null) ? focusableWeakCandidate : unfocusableWeakCandidate; 1112f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 1113f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 1114f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @Override 1115b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public boolean supportsPredictiveItemAnimations() { 1116204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar return mPendingSavedState == null && !mPendingSpanCountChange; 1117b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1118b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1119b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1120b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span. 1121b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1122b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final class DefaultSpanSizeLookup extends SpanSizeLookup { 1123061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar 1124b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 1125b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanSize(int position) { 1126b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return 1; 1127b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1128b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1129b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar @Override 1130b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex(int position, int spanCount) { 1131b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return position % spanCount; 1132b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1133b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1134b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1135b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1136b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * LayoutParams used by GridLayoutManager. 113742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * <p> 113842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the 113942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is 114042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar * expected to fill all of the space given to it. 1141b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1142b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static class LayoutParams extends RecyclerView.LayoutParams { 1143b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1144b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1145b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Span Id for Views that are not laid out yet. 1146b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1147b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public static final int INVALID_SPAN_ID = -1; 1148b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 11493a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas int mSpanIndex = INVALID_SPAN_ID; 1150b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 11513a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas int mSpanSize = 0; 1152b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1153b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(Context c, AttributeSet attrs) { 1154b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(c, attrs); 1155b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1156b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1157b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(int width, int height) { 1158b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(width, height); 1159b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1160b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1161b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(ViewGroup.MarginLayoutParams source) { 1162b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 1163b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1164b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1165b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(ViewGroup.LayoutParams source) { 1166b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 1167b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1168b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1169b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public LayoutParams(RecyclerView.LayoutParams source) { 1170b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar super(source); 1171b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1172b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1173b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1174b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the current span index of this View. If the View is not laid out yet, the return 1175b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * value is <code>undefined</code>. 1176b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 117795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar * Starting with RecyclerView <b>24.2.0</b>, span indices are always indexed from position 0 117895017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar * even if the layout is RTL. In a vertical GridLayoutManager, <b>leftmost</b> span is span 117995017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar * 0 if the layout is <b>LTR</b> and <b>rightmost</b> span is span 0 if the layout is 118095017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar * <b>RTL</b>. Prior to 24.2.0, it was the opposite which was conflicting with 118195017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar * {@link SpanSizeLookup#getSpanIndex(int, int)}. 1182b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p> 1183b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * If the View occupies multiple spans, span with the minimum index is returned. 1184b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 1185b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The span index of the View. 1186b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1187b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanIndex() { 1188b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanIndex; 1189b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1190b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1191b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar /** 1192b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Returns the number of spans occupied by this View. If the View not laid out yet, the 1193b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * return value is <code>undefined</code>. 1194b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * 1195b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * @return The number of spans occupied by this View. 1196b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */ 1197b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar public int getSpanSize() { 1198b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return mSpanSize; 1199b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1200b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar } 1201b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar 1202b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar} 1203