GridLayoutManager.java revision 0194ed84ad6f1d3d489db52b9431fa93a7697b50
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);
45204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    /**
46204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar     * Span size have been changed but we've not done a new layout calculation.
47204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar     */
48204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    boolean mPendingSpanCountChange = false;
49b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    int mSpanCount = DEFAULT_SPAN_COUNT;
50b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
517e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko     * Right borders for each span.
527e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko     * <p>For <b>i-th</b> item start is {@link #mCachedBorders}[i-1] + 1
537e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko     * and end is {@link #mCachedBorders}[i].
54b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
557e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    int [] mCachedBorders;
56b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
57b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Temporary array to keep views in layoutChunk method
58b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
59b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    View[] mSet;
609ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray();
61061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray();
62b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();
635e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar    // re-used variable to acquire decor insets from RecyclerView
645e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar    final Rect mDecorInsets = new Rect();
655e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar
660194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta
670194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta    /**
680194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * Constructor used when layout manager is set in XML by RecyclerView attribute
690194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * "layoutManager". If spanCount is not specified in the XML, it defaults to a
700194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * single column.
710194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     *
720194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
730194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     */
740194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta    public GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
750194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta                             int defStyleRes) {
760194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta        super(context, attrs, defStyleAttr, defStyleRes);
770194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta        Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
780194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta        setSpanCount(properties.spanCount);
790194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta    }
800194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta
815f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar    /**
825f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * Creates a vertical GridLayoutManager
835f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     *
845f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param context Current context, will be used to access resources.
855f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param spanCount The number of columns in the grid
865f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     */
87b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public GridLayoutManager(Context context, int spanCount) {
88b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        super(context);
89b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        setSpanCount(spanCount);
90b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
91b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
925f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar    /**
935f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param context Current context, will be used to access resources.
945f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param spanCount The number of columns or rows in the grid
955f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
965f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     *                      #VERTICAL}.
975f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param reverseLayout When set to true, layouts from end to start.
985f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     */
99b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public GridLayoutManager(Context context, int spanCount, int orientation,
100b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            boolean reverseLayout) {
101b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        super(context, orientation, reverseLayout);
102b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        setSpanCount(spanCount);
103b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
104b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1055f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar    /**
1065f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * stackFromEnd is not supported by GridLayoutManager. Consider using
1075f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * {@link #setReverseLayout(boolean)}.
1085f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     */
109b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
110061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void setStackFromEnd(boolean stackFromEnd) {
1115f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar        if (stackFromEnd) {
1125f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar            throw new UnsupportedOperationException(
1135f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar                    "GridLayoutManager does not support stack from end."
1145f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar                            + " Consider using reverse layout");
1155f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar        }
1165f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar        super.setStackFromEnd(false);
117061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
118061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
119061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
120a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
121a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            RecyclerView.State state) {
122a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (mOrientation == HORIZONTAL) {
123a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return mSpanCount;
124a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
125a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (state.getItemCount() < 1) {
126a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return 0;
127a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
128a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
129a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
130a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
131a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    @Override
132a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
133a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            RecyclerView.State state) {
134a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (mOrientation == VERTICAL) {
135a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return mSpanCount;
136a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
137a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (state.getItemCount() < 1) {
138a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return 0;
139a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
140a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
141a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
142a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
143a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    @Override
144a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
145a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
146a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        ViewGroup.LayoutParams lp = host.getLayoutParams();
147a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (!(lp instanceof LayoutParams)) {
148a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            super.onInitializeAccessibilityNodeInfoForItem(host, info);
149a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return;
150a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
151a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        LayoutParams glp = (LayoutParams) lp;
152115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition());
153a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (mOrientation == HORIZONTAL) {
154a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
155a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    glp.getSpanIndex(), glp.getSpanSize(),
156a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    spanGroupIndex, 1,
157a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
158a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        } else { // VERTICAL
159a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
160a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    spanGroupIndex , 1,
161a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    glp.getSpanIndex(), glp.getSpanSize(),
162a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
163a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
164a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
165a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
166a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    @Override
167b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1689ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (state.isPreLayout()) {
169061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            cachePreLayoutSpanMapping();
1709ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
171b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        super.onLayoutChildren(recycler, state);
172b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (DEBUG) {
173b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            validateChildOrder();
174b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
175061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        clearPreLayoutSpanMappingCache();
176204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        if (!state.isPreLayout()) {
177204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            mPendingSpanCountChange = false;
178204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        }
179061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
180061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
181061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private void clearPreLayoutSpanMappingCache() {
1829ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        mPreLayoutSpanSizeCache.clear();
183061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mPreLayoutSpanIndexCache.clear();
1849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    }
1859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
186061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private void cachePreLayoutSpanMapping() {
1879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        final int childCount = getChildCount();
188061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < childCount; i++) {
1899ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
190115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            final int viewPosition = lp.getViewLayoutPosition();
191061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize());
192061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex());
1939ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
194b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
195b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
196b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
197061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
198061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
199061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
200061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
201061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
202061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsChanged(RecyclerView recyclerView) {
203061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
204061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
205061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
206061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
207061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
208061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
209061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
210061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
211061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
212061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
213061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
214061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
215061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
216061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
217061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
218061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
219061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
220061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
221061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
222b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
223b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
224b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                ViewGroup.LayoutParams.WRAP_CONTENT);
225b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
226b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
227b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
228b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
229b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return new LayoutParams(c, attrs);
230b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
231b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
232b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
233b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
234b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (lp instanceof ViewGroup.MarginLayoutParams) {
235b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
236b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        } else {
237b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return new LayoutParams(lp);
238b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
239b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
240b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
241b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
242b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
243b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return lp instanceof LayoutParams;
244b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
245b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
246b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
247b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Sets the source to get the number of spans occupied by each item in the adapter.
248b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
249b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans
250061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar     *                       occupied by each item
251b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
252b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
253b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        mSpanSizeLookup = spanSizeLookup;
254b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
255b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
256b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
257b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager.
258b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
259b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @return The current {@link SpanSizeLookup} used by the GridLayoutManager.
260b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
261b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public SpanSizeLookup getSpanSizeLookup() {
262b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return mSpanSizeLookup;
263b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
264b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
265b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    private void updateMeasurements() {
266b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int totalSpace;
267b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (getOrientation() == VERTICAL) {
268b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
269b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        } else {
270b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
271b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
2727e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        calculateItemBorders(totalSpace);
2737e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
2747e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
2757e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    private void calculateItemBorders(int totalSpace) {
2767e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        if (mCachedBorders == null || mCachedBorders.length != mSpanCount + 1
2777e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                || mCachedBorders[mCachedBorders.length - 1] != totalSpace) {
2787e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            mCachedBorders = new int[mSpanCount + 1];
2797e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
2807e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        mCachedBorders[0] = 0;
2817e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int sizePerSpan = totalSpace / mSpanCount;
2827e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int sizePerSpanRemainder = totalSpace % mSpanCount;
2837e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int consumedPixels = 0;
2847e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int additionalSize = 0;
2857e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (int i = 1; i <= mSpanCount; i++) {
2867e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            int itemSize = sizePerSpan;
2877e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            additionalSize += sizePerSpanRemainder;
2887e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            if (additionalSize > 0 && (mSpanCount - additionalSize) < sizePerSpanRemainder) {
2897e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                itemSize += 1;
2907e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                additionalSize -= mSpanCount;
2917e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            }
2927e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            consumedPixels += itemSize;
2937e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            mCachedBorders[i] = consumedPixels;
2947e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
295b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
296b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
297b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
2989ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) {
2999ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        super.onAnchorReady(state, anchorInfo);
300b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        updateMeasurements();
301061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (state.getItemCount() > 0 && !state.isPreLayout()) {
3029ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            ensureAnchorIsInFirstSpan(anchorInfo);
3039ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
3049ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (mSet == null || mSet.length != mSpanCount) {
3059ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            mSet = new View[mSpanCount];
3069ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
3079ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    }
3089ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
3099ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) {
310061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
31173304eff156157f62075215e795e774803a6f96aYigit Boyar        while (span > 0 && anchorInfo.mPosition > 0) {
312061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            anchorInfo.mPosition--;
313061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
314061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
315061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
316061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
3171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    @Override
3181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    View findReferenceChild(int start, int end, int itemCount) {
3191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        ensureLayoutState();
3201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        View invalidMatch = null;
3211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        View outOfBoundsMatch = null;
3221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int boundsStart = mOrientationHelper.getStartAfterPadding();
3231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int boundsEnd = mOrientationHelper.getEndAfterPadding();
3241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int diff = end > start ? 1 : -1;
3251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = start; i != end; i += diff) {
3261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final View view = getChildAt(i);
3271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int position = getPosition(view);
3281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (position >= 0 && position < itemCount) {
3291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int span = mSpanSizeLookup.getCachedSpanIndex(position, mSpanCount);
3301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (span != 0) {
3311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    continue;
3321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
3331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
3341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    if (invalidMatch == null) {
3351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        invalidMatch = view; // removed item, least preferred
3361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    }
3371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||
3381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
3391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    if (outOfBoundsMatch == null) {
3401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        outOfBoundsMatch = view; // item is not visible, less preferred
3411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    }
3421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
3431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    return view;
3441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
3451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
3461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
3471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
3481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
3491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
350a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state,
351a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int viewPosition) {
352a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (!state.isPreLayout()) {
353a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount);
354a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
355a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition);
356a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (adapterPosition == -1) {
357a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            if (DEBUG) {
358a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                throw new RuntimeException("Cannot find span group index for position "
359a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                        + viewPosition);
360a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
361a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition);
362a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return 0;
363a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
364a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount);
365a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
366a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
367061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
368061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (!state.isPreLayout()) {
369061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);
370061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
371061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int cached = mPreLayoutSpanIndexCache.get(pos, -1);
372061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (cached != -1) {
373061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return cached;
374061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
375061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
376061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (adapterPosition == -1) {
377061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (DEBUG) {
378061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                throw new RuntimeException("Cannot find span index for pre layout position. It is"
379061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        + " not cached, not in the adapter. Pos:" + pos);
380061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
381061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            Log.w(TAG, "Cannot find span size for pre layout position. It is"
382061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    + " not cached, not in the adapter. Pos:" + pos);
383061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return 0;
384b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
385061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);
3869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    }
3879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
3889ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
3899ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (!state.isPreLayout()) {
3909ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            return mSpanSizeLookup.getSpanSize(pos);
391b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
3929ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        final int cached = mPreLayoutSpanSizeCache.get(pos, -1);
3939ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (cached != -1) {
3949ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            return cached;
3959ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
3969ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
3979ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (adapterPosition == -1) {
3989ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            if (DEBUG) {
3999ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                throw new RuntimeException("Cannot find span size for pre layout position. It is"
4009ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                        + " not cached, not in the adapter. Pos:" + pos);
4019ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            }
4029ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            Log.w(TAG, "Cannot find span size for pre layout position. It is"
4039ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    + " not cached, not in the adapter. Pos:" + pos);
4049ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            return 1;
4059ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
4069ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        return mSpanSizeLookup.getSpanSize(adapterPosition);
407b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
408b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
409b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
410b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
411b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            LayoutState layoutState, LayoutChunkResult result) {
412061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final boolean layingOutInPrimaryDirection =
413061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
414b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int count = 0;
415061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int consumedSpanCount = 0;
416b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int remainingSpan = mSpanCount;
417061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (!layingOutInPrimaryDirection) {
418061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
419061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
420061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            remainingSpan = itemSpanIndex + itemSpanSize;
421061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
422b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
423b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            int pos = layoutState.mCurrentPosition;
4249ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            final int spanSize = getSpanSize(recycler, state, pos);
42573304eff156157f62075215e795e774803a6f96aYigit Boyar            if (spanSize > mSpanCount) {
42673304eff156157f62075215e795e774803a6f96aYigit Boyar                throw new IllegalArgumentException("Item at position " + pos + " requires " +
42773304eff156157f62075215e795e774803a6f96aYigit Boyar                        spanSize + " spans but GridLayoutManager has only " + mSpanCount
42873304eff156157f62075215e795e774803a6f96aYigit Boyar                        + " spans.");
42973304eff156157f62075215e795e774803a6f96aYigit Boyar            }
430b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            remainingSpan -= spanSize;
431b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (remainingSpan < 0) {
432b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                break; // item did not fit into this row or column
433b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
434b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            View view = layoutState.next(recycler);
435b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (view == null) {
436b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                break;
437b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
438061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            consumedSpanCount += spanSize;
439b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            mSet[count] = view;
440061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            count++;
441b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
442b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
443b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (count == 0) {
444b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            result.mFinished = true;
445b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return;
446b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
447b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
448b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int maxSize = 0;
449061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
4505e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        // we should assign spans before item decor offsets are calculated
451061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
452061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < count; i++) {
453b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            View view = mSet[i];
4549ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            if (layoutState.mScrapList == null) {
4559ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                if (layingOutInPrimaryDirection) {
4569ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addView(view);
4579ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                } else {
4589ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addView(view, 0);
4599ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                }
460b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
4619ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                if (layingOutInPrimaryDirection) {
4629ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addDisappearingView(view);
4639ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                } else {
4649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addDisappearingView(view, 0);
4659ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                }
466b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
4679ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4697e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            final int spec = View.MeasureSpec.makeMeasureSpec(
4707e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
4717e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                            mCachedBorders[lp.mSpanIndex],
4727e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    View.MeasureSpec.EXACTLY);
473b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (mOrientation == VERTICAL) {
474061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height));
475b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
476061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec);
477b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
478b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            final int size = mOrientationHelper.getDecoratedMeasurement(view);
479b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (size > maxSize) {
480b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                maxSize = size;
481b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
482b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
483afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
484afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        // views that did not measure the maxSize has to be re-measured
485afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final int maxMeasureSpec = getMainDirSpec(maxSize);
486afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (int i = 0; i < count; i ++) {
487afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            final View view = mSet[i];
488afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
4897e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4907e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                final int spec = View.MeasureSpec.makeMeasureSpec(
4917e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                        mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
4927e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                                mCachedBorders[lp.mSpanIndex],
493afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        View.MeasureSpec.EXACTLY);
494afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                if (mOrientation == VERTICAL) {
495afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec);
496afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                } else {
497afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec);
498afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                }
499afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            }
500afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
501afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
502b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        result.mConsumed = maxSize;
503b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
5045e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        int left = 0, right = 0, top = 0, bottom = 0;
505b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (mOrientation == VERTICAL) {
506b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
507b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                bottom = layoutState.mOffset;
508b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                top = bottom - maxSize;
509b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
510b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                top = layoutState.mOffset;
511b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                bottom = top + maxSize;
512b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
513b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        } else {
514b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
515b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                right = layoutState.mOffset;
516b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                left = right - maxSize;
517b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
518b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                left = layoutState.mOffset;
519b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                right = left + maxSize;
520b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
521b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
522061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < count; i++) {
523b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            View view = mSet[i];
524b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            LayoutParams params = (LayoutParams) view.getLayoutParams();
525b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (mOrientation == VERTICAL) {
5267e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
527b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
528b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
5297e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                top = getPaddingTop() + mCachedBorders[params.mSpanIndex];
530b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
531b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
532b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            // We calculate everything with View's bounding box (which includes decor and margins)
533b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            // To calculate correct layout position, we subtract margins.
534b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
535b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    right - params.rightMargin, bottom - params.bottomMargin);
536b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (DEBUG) {
537b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
538b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
539b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)
5405e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar                        + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize);
541b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
542b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            // Consume the available space if the view is not removed OR changed
543b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (params.isItemRemoved() || params.isItemChanged()) {
544b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                result.mIgnoreConsumed = true;
545b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
546b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            result.mFocusable |= view.isFocusable();
547b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
548b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        Arrays.fill(mSet, null);
549b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
550b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
551061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private int getMainDirSpec(int dim) {
552061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (dim < 0) {
553061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return MAIN_DIR_SPEC;
554061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        } else {
555061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);
556061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
557061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
558061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
559b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {
5605e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        calculateItemDecorationsForChild(child, mDecorInsets);
561b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
5625e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left,
5635e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar                lp.rightMargin + mDecorInsets.right);
5645e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top,
5655e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar                lp.bottomMargin + mDecorInsets.bottom);
566b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        child.measure(widthSpec, heightSpec);
567b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
568b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
569b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    private int updateSpecWithExtra(int spec, int startInset, int endInset) {
570b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (startInset == 0 && endInset == 0) {
571b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return spec;
572b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
573b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        final int mode = View.MeasureSpec.getMode(spec);
574b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
575b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return View.MeasureSpec.makeMeasureSpec(
576b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
577b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
578b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return spec;
579b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
580b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
5819ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
582061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int consumedSpanCount, boolean layingOutInPrimaryDirection) {
5835e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        int span, spanDiff, start, end, diff;
5845e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        // make sure we traverse from min position to max position
5855e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        if (layingOutInPrimaryDirection) {
5865e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            start = 0;
5875e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            end = count;
5885e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            diff = 1;
5895e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        } else {
5905e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            start = count - 1;
5915e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            end = -1;
5925e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            diff = -1;
5935e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        }
5945e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span
595871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            span = mSpanCount - 1;
5965e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            spanDiff = -1;
5975e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        } else {
5985e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            span = 0;
5995e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            spanDiff = 1;
6005e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        }
6015e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        for (int i = start; i != end; i += diff) {
6025e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            View view = mSet[i];
6035e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            LayoutParams params = (LayoutParams) view.getLayoutParams();
6049ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            params.mSpanSize = getSpanSize(recycler, state, getPosition(view));
6055e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            if (spanDiff == -1 && params.mSpanSize > 1) {
6065e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar                params.mSpanIndex = span - (params.mSpanSize - 1);
6075e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            } else {
6085e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar                params.mSpanIndex = span;
6095e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            }
6105e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            span += spanDiff * params.mSpanSize;
6115e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        }
6125e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar    }
6135e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar
614b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
615b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Returns the number of spans laid out by this grid.
616b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
617b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @return The number of spans
618b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @see #setSpanCount(int)
619b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
620b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public int getSpanCount() {
621b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return mSpanCount;
622b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
623b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
624b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
625b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Sets the number of spans to be laid out.
626b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * <p>
627b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns.
628b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows.
629b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
630b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @param spanCount The total number of spans in the grid
631b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @see #getSpanCount()
632b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
633b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public void setSpanCount(int spanCount) {
634b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (spanCount == mSpanCount) {
635b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return;
636b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
637204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mPendingSpanCountChange = true;
638b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (spanCount < 1) {
639b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            throw new IllegalArgumentException("Span count should be at least 1. Provided "
640b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    + spanCount);
641b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
642b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        mSpanCount = spanCount;
643061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
644b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
645b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
646b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
647b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * A helper class to provide the number of spans each item occupies.
648b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * <p>
649b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Default implementation sets each item to occupy exactly 1 span.
650b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
651b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
652b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
653b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public static abstract class SpanSizeLookup {
654061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
655061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final SparseIntArray mSpanIndexCache = new SparseIntArray();
656061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
657061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        private boolean mCacheSpanIndices = false;
658061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
659b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
660b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the number of span occupied by the item at <code>position</code>.
661b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
662b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @param position The adapter position of the item
663b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The number of spans occupied by the item at the provided position
664b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
665b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        abstract public int getSpanSize(int position);
666b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
667b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
668061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
669061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * not. By default these values are not cached. If you are not overriding
670061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
671061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         *
672061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
673061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         */
674061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
675061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mCacheSpanIndices = cacheSpanIndices;
676061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
677061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
678061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        /**
679061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Clears the span index cache. GridLayoutManager automatically calls this method when
680061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * adapter changes occur.
681061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         */
682061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void invalidateSpanIndexCache() {
683061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanIndexCache.clear();
684061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
685061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
686061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        /**
687061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
688061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         *
689061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * @return True if results of {@link #getSpanIndex(int, int)} are cached.
690061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         */
691061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public boolean isSpanIndexCacheEnabled() {
692061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return mCacheSpanIndices;
693061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
694061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
695061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int getCachedSpanIndex(int position, int spanCount) {
696061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (!mCacheSpanIndices) {
697061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return getSpanIndex(position, spanCount);
698061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
699061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            final int existing = mSpanIndexCache.get(position, -1);
700061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (existing != -1) {
701061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return existing;
702061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
703061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            final int value = getSpanIndex(position, spanCount);
704061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanIndexCache.put(position, value);
705061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return value;
706061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
707061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
708061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        /**
709b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the final span index of the provided position.
710b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
711061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * If you have a faster way to calculate span index for your items, you should override
712061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * this method. Otherwise, you should enable span index cache
713061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
714061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * disabled, default implementation traverses all items from 0 to
715061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * <code>position</code>. When caching is enabled, it calculates from the closest cached
716061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * value before the <code>position</code>.
717061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * <p>
718061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * If you override this method, you need to make sure it is consistent with
719a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for
720061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * each item. It is called only for the reference item and rest of the items
721061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * are assigned to spans based on the reference item. For example, you cannot assign a
722061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * position to span 2 while span 1 is empty.
723b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
724061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Note that span offsets always start with 0 and are not affected by RTL.
725b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
726061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * @param position  The position of the item
727b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @param spanCount The total number of spans in the grid
728b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The final span position of the item. Should be between 0 (inclusive) and
729b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <code>spanCount</code>(exclusive)
730b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
731b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanIndex(int position, int spanCount) {
732b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            int positionSpanSize = getSpanSize(position);
733b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (positionSpanSize == spanCount) {
734b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                return 0; // quick return for full-span items
735b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
73673304eff156157f62075215e795e774803a6f96aYigit Boyar            int span = 0;
737061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int startPos = 0;
738061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            // If caching is enabled, try to jump
739061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
740061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int prevKey = findReferenceIndexFromCache(position);
741061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (prevKey >= 0) {
742061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
743061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    startPos = prevKey + 1;
744061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
745061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
746061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (int i = startPos; i < position; i++) {
74773304eff156157f62075215e795e774803a6f96aYigit Boyar                int size = getSpanSize(i);
748b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                span += size;
749b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                if (span == spanCount) {
750b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    span = 0;
751b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                } else if (span > spanCount) {
752b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    // did not fit, moving to next row / column
753b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    span = size;
754b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                }
755b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
756b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (span + positionSpanSize <= spanCount) {
757b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                return span;
758b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
759b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return 0;
760b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
761061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
762061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int findReferenceIndexFromCache(int position) {
763061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int lo = 0;
764061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int hi = mSpanIndexCache.size() - 1;
765061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
766061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            while (lo <= hi) {
767061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                final int mid = (lo + hi) >>> 1;
768061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                final int midVal = mSpanIndexCache.keyAt(mid);
769061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (midVal < position) {
770061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    lo = mid + 1;
771061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                } else {
772061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    hi = mid - 1;
773061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
774061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
775061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int index = lo - 1;
776061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (index >= 0 && index < mSpanIndexCache.size()) {
777061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return mSpanIndexCache.keyAt(index);
778061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
779061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return -1;
780061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
781a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
782a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        /**
783a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * Returns the index of the group this position belongs.
784a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * <p>
785a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * For example, if grid has 3 columns and each item occupies 1 span, span group index
786a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * for item 1 will be 0, item 5 will be 1.
787a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         *
788a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * @param adapterPosition The position in adapter
789a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * @param spanCount The total number of spans in the grid
790a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * @return The index of the span group including the item at the given adapter position
791a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         */
792a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        public int getSpanGroupIndex(int adapterPosition, int spanCount) {
793a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int span = 0;
794a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int group = 0;
795a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int positionSpanSize = getSpanSize(adapterPosition);
796a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            for (int i = 0; i < adapterPosition; i++) {
797a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                int size = getSpanSize(i);
798a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                span += size;
799a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (span == spanCount) {
800a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    span = 0;
801a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    group++;
802a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                } else if (span > spanCount) {
803a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    // did not fit, moving to next row / column
804a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    span = size;
805a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    group++;
806a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
807a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
808a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            if (span + positionSpanSize > spanCount) {
809a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                group++;
810a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
811a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return group;
812a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
813b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
814b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
815b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
816b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public boolean supportsPredictiveItemAnimations() {
817204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        return mPendingSavedState == null && !mPendingSpanCountChange;
818b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
819b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
820b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
821b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
822b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
823b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
824061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
825b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        @Override
826b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanSize(int position) {
827b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return 1;
828b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
829b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
830b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        @Override
831b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanIndex(int position, int spanCount) {
832b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return position % spanCount;
833b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
834b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
835b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
836b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
837b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * LayoutParams used by GridLayoutManager.
83842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * <p>
83942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the
84042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is
84142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * expected to fill all of the space given to it.
842b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
843b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public static class LayoutParams extends RecyclerView.LayoutParams {
844b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
845b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
846b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Span Id for Views that are not laid out yet.
847b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
848b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public static final int INVALID_SPAN_ID = -1;
849b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
850b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        private int mSpanIndex = INVALID_SPAN_ID;
851b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
852b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        private int mSpanSize = 0;
853b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
854b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(Context c, AttributeSet attrs) {
855b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(c, attrs);
856b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
857b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
858b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(int width, int height) {
859b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(width, height);
860b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
861b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
862b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(ViewGroup.MarginLayoutParams source) {
863b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(source);
864b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
865b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
866b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(ViewGroup.LayoutParams source) {
867b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(source);
868b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
869b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
870b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(RecyclerView.LayoutParams source) {
871b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(source);
872b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
873b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
874b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
875b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the current span index of this View. If the View is not laid out yet, the return
876b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * value is <code>undefined</code>.
877b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
878b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Note that span index may change by whether the RecyclerView is RTL or not. For
879b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * example, if the number of spans is 3 and layout is RTL, the rightmost item will have
880b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * span index of 2. If the layout changes back to LTR, span index for this view will be 0.
881b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * If the item was occupying 2 spans, span indices would be 1 and 0 respectively.
882b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
883b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * If the View occupies multiple spans, span with the minimum index is returned.
884b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
885b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The span index of the View.
886b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
887b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanIndex() {
888b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return mSpanIndex;
889b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
890b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
891b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
892b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the number of spans occupied by this View. If the View not laid out yet, the
893b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * return value is <code>undefined</code>.
894b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
895b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The number of spans occupied by this View.
896b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
897b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanSize() {
898b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return mSpanSize;
899b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
900b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
901b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
902b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar}
903