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