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