1b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar/*
2b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
3b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar *
4b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * you may not use this file except in compliance with the License.
6b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * You may obtain a copy of the License at
7b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar *
8b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar *
10b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * See the License for the specific language governing permissions and
14b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * limitations under the License.
15b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */
16b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarpackage android.support.v7.widget;
17b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
18b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.content.Context;
19b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.graphics.Rect;
20a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
21b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.util.AttributeSet;
22b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.util.Log;
239ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyarimport android.util.SparseIntArray;
24b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.view.View;
25b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport android.view.ViewGroup;
26b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
27b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarimport java.util.Arrays;
28b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
29b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar/**
30b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid.
31b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * <p>
32b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * By default, each item occupies 1 span. You can change it by providing a custom
33b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.
34b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar */
35b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyarpublic class GridLayoutManager extends LinearLayoutManager {
36b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
37b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    private static final boolean DEBUG = false;
38b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    private static final String TAG = "GridLayoutManager";
39b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public static final int DEFAULT_SPAN_COUNT = -1;
40061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    /**
41204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar     * Span size have been changed but we've not done a new layout calculation.
42204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar     */
43204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    boolean mPendingSpanCountChange = false;
44b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    int mSpanCount = DEFAULT_SPAN_COUNT;
45b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
467e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko     * Right borders for each span.
477e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko     * <p>For <b>i-th</b> item start is {@link #mCachedBorders}[i-1] + 1
487e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko     * and end is {@link #mCachedBorders}[i].
49b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
507e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    int [] mCachedBorders;
51b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
52b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Temporary array to keep views in layoutChunk method
53b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
54b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    View[] mSet;
559ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray();
56061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray();
57b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();
585e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar    // re-used variable to acquire decor insets from RecyclerView
595e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar    final Rect mDecorInsets = new Rect();
605e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar
610194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta
620194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta    /**
630194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * Constructor used when layout manager is set in XML by RecyclerView attribute
640194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * "layoutManager". If spanCount is not specified in the XML, it defaults to a
650194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * single column.
660194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     *
670194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
680194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta     */
690194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta    public GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
700194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta                             int defStyleRes) {
710194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta        super(context, attrs, defStyleAttr, defStyleRes);
720194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta        Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
730194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta        setSpanCount(properties.spanCount);
740194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta    }
750194ed84ad6f1d3d489db52b9431fa93a7697b50Deepanshu Gupta
765f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar    /**
775f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * Creates a vertical GridLayoutManager
785f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     *
795f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param context Current context, will be used to access resources.
805f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param spanCount The number of columns in the grid
815f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     */
82b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public GridLayoutManager(Context context, int spanCount) {
83b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        super(context);
84b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        setSpanCount(spanCount);
85b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
86b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
875f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar    /**
885f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param context Current context, will be used to access resources.
895f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param spanCount The number of columns or rows in the grid
905f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
915f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     *                      #VERTICAL}.
925f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * @param reverseLayout When set to true, layouts from end to start.
935f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     */
94b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public GridLayoutManager(Context context, int spanCount, int orientation,
95b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            boolean reverseLayout) {
96b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        super(context, orientation, reverseLayout);
97b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        setSpanCount(spanCount);
98b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
99b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1005f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar    /**
1015f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * stackFromEnd is not supported by GridLayoutManager. Consider using
1025f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     * {@link #setReverseLayout(boolean)}.
1035f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar     */
104b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
105061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void setStackFromEnd(boolean stackFromEnd) {
1065f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar        if (stackFromEnd) {
1075f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar            throw new UnsupportedOperationException(
1085f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar                    "GridLayoutManager does not support stack from end."
1095f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar                            + " Consider using reverse layout");
1105f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar        }
1115f1c90f3afcf680f6467d80eb369f81f35222ed3Yigit Boyar        super.setStackFromEnd(false);
112061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
113061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
114061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
115a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
116a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            RecyclerView.State state) {
117a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (mOrientation == HORIZONTAL) {
118a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return mSpanCount;
119a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
120a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (state.getItemCount() < 1) {
121a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return 0;
122a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
12356d2535547810327746f8306dbd1cf10a3f10870Steven Dao
12456d2535547810327746f8306dbd1cf10a3f10870Steven Dao        // Row count is one more than the last item's row index.
12556d2535547810327746f8306dbd1cf10a3f10870Steven Dao        return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
126a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
127a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
128a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    @Override
129a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
130a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            RecyclerView.State state) {
131a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (mOrientation == VERTICAL) {
132a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return mSpanCount;
133a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
134a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (state.getItemCount() < 1) {
135a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return 0;
136a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
13756d2535547810327746f8306dbd1cf10a3f10870Steven Dao
13856d2535547810327746f8306dbd1cf10a3f10870Steven Dao        // Column count is one more than the last item's column index.
13956d2535547810327746f8306dbd1cf10a3f10870Steven Dao        return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
140a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
141a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
142a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    @Override
143a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
144a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
145a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        ViewGroup.LayoutParams lp = host.getLayoutParams();
146a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (!(lp instanceof LayoutParams)) {
147a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            super.onInitializeAccessibilityNodeInfoForItem(host, info);
148a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return;
149a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
150a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        LayoutParams glp = (LayoutParams) lp;
151115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition());
152a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (mOrientation == HORIZONTAL) {
153a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
154a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    glp.getSpanIndex(), glp.getSpanSize(),
155a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    spanGroupIndex, 1,
156a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
157a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        } else { // VERTICAL
158a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
159a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    spanGroupIndex , 1,
160a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    glp.getSpanIndex(), glp.getSpanSize(),
161a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
162a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
163a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
164a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
165a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    @Override
166b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1679ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (state.isPreLayout()) {
168061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            cachePreLayoutSpanMapping();
1699ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
170b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        super.onLayoutChildren(recycler, state);
171b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (DEBUG) {
172b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            validateChildOrder();
173b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
174061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        clearPreLayoutSpanMappingCache();
1758cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar    }
1768cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar
1778cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar    @Override
1788cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar    public void onLayoutCompleted(RecyclerView.State state) {
1798cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar        super.onLayoutCompleted(state);
1808cf399b2e813234a1a603df3575c2dcdfa38dc9eYigit Boyar        mPendingSpanCountChange = false;
181061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
182061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
183061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private void clearPreLayoutSpanMappingCache() {
1849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        mPreLayoutSpanSizeCache.clear();
185061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mPreLayoutSpanIndexCache.clear();
1869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    }
1879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
188061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private void cachePreLayoutSpanMapping() {
1899ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        final int childCount = getChildCount();
190061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < childCount; i++) {
1919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
192115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            final int viewPosition = lp.getViewLayoutPosition();
193061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize());
194061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex());
1959ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
196b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
197b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
198b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
199061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
200061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
201061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
202061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
203061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
204061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsChanged(RecyclerView recyclerView) {
205061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
206061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
207061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
208061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
209061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
210061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
211061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
212061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
213061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
21421b345f101abc385496f42d250e580d21f1287b6Dake Gu    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
21521b345f101abc385496f42d250e580d21f1287b6Dake Gu            Object payload) {
216061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
217061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
218061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
219061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
220061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
221061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
222061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
223061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
224061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
225b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
2264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (mOrientation == HORIZONTAL) {
2274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
228e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    ViewGroup.LayoutParams.MATCH_PARENT);
2294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        } else {
230e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas            return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
2314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    ViewGroup.LayoutParams.WRAP_CONTENT);
2324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
233b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
234b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
235b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
236b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
237b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return new LayoutParams(c, attrs);
238b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
239b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
240b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
241b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
242b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (lp instanceof ViewGroup.MarginLayoutParams) {
243b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
244b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        } else {
245b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return new LayoutParams(lp);
246b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
247b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
248b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
249b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
250b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
251b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return lp instanceof LayoutParams;
252b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
253b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
254b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
255b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Sets the source to get the number of spans occupied by each item in the adapter.
256b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
257b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans
258061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar     *                       occupied by each item
259b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
260b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
261b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        mSpanSizeLookup = spanSizeLookup;
262b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
263b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
264b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
265b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager.
266b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
267b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @return The current {@link SpanSizeLookup} used by the GridLayoutManager.
268b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
269b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public SpanSizeLookup getSpanSizeLookup() {
270b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return mSpanSizeLookup;
271b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
272b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
273b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    private void updateMeasurements() {
274b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int totalSpace;
275b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (getOrientation() == VERTICAL) {
276b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
277b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        } else {
278b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
279b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
2807e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        calculateItemBorders(totalSpace);
2817e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
2827e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
2834143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    @Override
2844143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
2854143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (mCachedBorders == null) {
2864143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            super.setMeasuredDimension(childrenBounds, wSpec, hSpec);
2874143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
2884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        final int width, height;
2896b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar        final int horizontalPadding = getPaddingLeft() + getPaddingRight();
2906b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar        final int verticalPadding = getPaddingTop() + getPaddingBottom();
2914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (mOrientation == VERTICAL) {
2926b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar            final int usedHeight = childrenBounds.height() + verticalPadding;
2934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            height = chooseSize(hSpec, usedHeight, getMinimumHeight());
2946b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar            width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding,
2954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    getMinimumWidth());
2964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        } else {
2976b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar            final int usedWidth = childrenBounds.width() + horizontalPadding;
2984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            width = chooseSize(wSpec, usedWidth, getMinimumWidth());
2996b6a29eea7f6a212447b3cc7b45a081b609ca4b1Yigit Boyar            height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding,
3004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    getMinimumHeight());
3017e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
3024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        setMeasuredDimension(width, height);
3034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
3044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
3054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    /**
3064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @param totalSpace Total available space after padding is removed
3074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     */
3084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    private void calculateItemBorders(int totalSpace) {
3094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        mCachedBorders = calculateItemBorders(mCachedBorders, mSpanCount, totalSpace);
3104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
3114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
3124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    /**
3134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @param cachedBorders The out array
3144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @param spanCount number of spans
3154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @param totalSpace total available space after padding is removed
3164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @return The updated array. Might be the same instance as the provided array if its size
3174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * has not changed.
3184143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     */
3194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) {
3204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (cachedBorders == null || cachedBorders.length != spanCount + 1
3214143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                || cachedBorders[cachedBorders.length - 1] != totalSpace) {
3224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            cachedBorders = new int[spanCount + 1];
3234143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
3244143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        cachedBorders[0] = 0;
3254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        int sizePerSpan = totalSpace / spanCount;
3264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        int sizePerSpanRemainder = totalSpace % spanCount;
3277e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int consumedPixels = 0;
3287e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int additionalSize = 0;
3294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        for (int i = 1; i <= spanCount; i++) {
3307e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            int itemSize = sizePerSpan;
3317e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            additionalSize += sizePerSpanRemainder;
3324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) {
3337e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                itemSize += 1;
3344143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                additionalSize -= spanCount;
3357e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            }
3367e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            consumedPixels += itemSize;
3374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            cachedBorders[i] = consumedPixels;
3387e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
3394143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        return cachedBorders;
340b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
341b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
34295017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar    int getSpaceForSpanRange(int startSpan, int spanSize) {
34395017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        if (mOrientation == VERTICAL && isLayoutRTL()) {
34495017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar            return mCachedBorders[mSpanCount - startSpan]
34595017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar                    - mCachedBorders[mSpanCount - startSpan - spanSize];
34695017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        } else {
34795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar            return mCachedBorders[startSpan + spanSize] - mCachedBorders[startSpan];
34895017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        }
34995017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar    }
35095017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar
351b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
352cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
353f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                       AnchorInfo anchorInfo, int itemDirection) {
354f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar        super.onAnchorReady(recycler, state, anchorInfo, itemDirection);
355b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        updateMeasurements();
356061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (state.getItemCount() > 0 && !state.isPreLayout()) {
357f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection);
3589ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
359bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        ensureViewSet();
360bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    }
361bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar
362bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    private void ensureViewSet() {
3639ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (mSet == null || mSet.length != mSpanCount) {
3649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            mSet = new View[mSpanCount];
3659ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
3669ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    }
3679ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
368bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    @Override
369bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
370bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar            RecyclerView.State state) {
371bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        updateMeasurements();
372bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        ensureViewSet();
373bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        return super.scrollHorizontallyBy(dx, recycler, state);
374bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    }
375bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar
376bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    @Override
377bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
378bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar            RecyclerView.State state) {
379bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        updateMeasurements();
380bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        ensureViewSet();
381bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        return super.scrollVerticallyBy(dy, recycler, state);
382bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    }
383bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar
384f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar    private void ensureAnchorIsInCorrectSpan(RecyclerView.Recycler recycler,
385f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            RecyclerView.State state, AnchorInfo anchorInfo, int itemDirection) {
386f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar        final boolean layingOutInPrimaryDirection =
387f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                itemDirection == LayoutState.ITEM_DIRECTION_TAIL;
388cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        int span = getSpanIndex(recycler, state, anchorInfo.mPosition);
389f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar        if (layingOutInPrimaryDirection) {
390f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            // choose span 0
391f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            while (span > 0 && anchorInfo.mPosition > 0) {
392f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                anchorInfo.mPosition--;
393f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                span = getSpanIndex(recycler, state, anchorInfo.mPosition);
394f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            }
395f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar        } else {
396f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            // choose the max span we can get. hopefully last one
397f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            final int indexLimit = state.getItemCount() - 1;
398f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            int pos = anchorInfo.mPosition;
399f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            int bestSpan = span;
400f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            while (pos < indexLimit) {
401f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                int next = getSpanIndex(recycler, state, pos + 1);
402f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                if (next > bestSpan) {
403f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                    pos += 1;
404f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                    bestSpan = next;
405f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                } else {
406f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                    break;
407f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar                }
408f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            }
409f7f1c1ef2a0e0696fbfa8065ac88cd1f8dd39623Yigit Boyar            anchorInfo.mPosition = pos;
410061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
411061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
412061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
4131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    @Override
414cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
415cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                            int start, int end, int itemCount) {
4161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        ensureLayoutState();
4171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        View invalidMatch = null;
4181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        View outOfBoundsMatch = null;
4191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int boundsStart = mOrientationHelper.getStartAfterPadding();
4201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int boundsEnd = mOrientationHelper.getEndAfterPadding();
4211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int diff = end > start ? 1 : -1;
42295017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar
4231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = start; i != end; i += diff) {
4241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final View view = getChildAt(i);
4251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int position = getPosition(view);
4261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (position >= 0 && position < itemCount) {
427cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                final int span = getSpanIndex(recycler, state, position);
4281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (span != 0) {
4291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    continue;
4301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
4311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
4321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    if (invalidMatch == null) {
4331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        invalidMatch = view; // removed item, least preferred
4341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    }
4351e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
4361e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                        || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
4371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    if (outOfBoundsMatch == null) {
4381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        outOfBoundsMatch = view; // item is not visible, less preferred
4391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    }
4401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
4411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    return view;
4421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
4431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
4441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
4451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
4461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
4471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
448a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state,
449a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int viewPosition) {
450a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (!state.isPreLayout()) {
451a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount);
452a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
453a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition);
454a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        if (adapterPosition == -1) {
455a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            if (DEBUG) {
456a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                throw new RuntimeException("Cannot find span group index for position "
457a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                        + viewPosition);
458a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
459a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition);
460a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return 0;
461a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
462a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount);
463a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
464a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
465061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
466061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (!state.isPreLayout()) {
467061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);
468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
469061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int cached = mPreLayoutSpanIndexCache.get(pos, -1);
470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (cached != -1) {
471061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return cached;
472061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
473061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
474061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (adapterPosition == -1) {
475061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (DEBUG) {
476061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                throw new RuntimeException("Cannot find span index for pre layout position. It is"
477061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        + " not cached, not in the adapter. Pos:" + pos);
478061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
479061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            Log.w(TAG, "Cannot find span size for pre layout position. It is"
480061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    + " not cached, not in the adapter. Pos:" + pos);
481061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return 0;
482b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
483061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);
4849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    }
4859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
4869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
4879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (!state.isPreLayout()) {
4889ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            return mSpanSizeLookup.getSpanSize(pos);
489b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
4909ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        final int cached = mPreLayoutSpanSizeCache.get(pos, -1);
4919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (cached != -1) {
4929ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            return cached;
4939ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
4949ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
4959ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        if (adapterPosition == -1) {
4969ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            if (DEBUG) {
4979ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                throw new RuntimeException("Cannot find span size for pre layout position. It is"
4989ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                        + " not cached, not in the adapter. Pos:" + pos);
4999ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            }
5009ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            Log.w(TAG, "Cannot find span size for pre layout position. It is"
5019ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    + " not cached, not in the adapter. Pos:" + pos);
5029ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            return 1;
5039ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        }
5049ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        return mSpanSizeLookup.getSpanSize(adapterPosition);
505b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
506b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
507b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
508945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik    void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
5093104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik            LayoutPrefetchRegistry layoutPrefetchRegistry) {
510ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        int remainingSpan = mSpanCount;
511ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        int count = 0;
512ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
513ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            final int pos = layoutState.mCurrentPosition;
5146425bbc0816bc5c2dbd14010d8dee4245d14fd1dChris Craik            layoutPrefetchRegistry.addPosition(pos, Math.max(0, layoutState.mScrollingOffset));
515ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            final int spanSize = mSpanSizeLookup.getSpanSize(pos);
516ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            remainingSpan -= spanSize;
517ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            layoutState.mCurrentPosition += layoutState.mItemDirection;
518ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            count++;
519ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        }
520ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik    }
521ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik
522ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik    @Override
523b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
524b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            LayoutState layoutState, LayoutChunkResult result) {
5254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        final int otherDirSpecMode = mOrientationHelper.getModeInOther();
5264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        final boolean flexibleInOtherDir = otherDirSpecMode != View.MeasureSpec.EXACTLY;
5274143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        final int currentOtherDirSize = getChildCount() > 0 ? mCachedBorders[mSpanCount] : 0;
5284143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        // if grid layout's dimensions are not specified, let the new row change the measurements
5294143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        // This is not perfect since we not covering all rows but still solves an important case
5304143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        // where they may have a header row which should be laid out according to children.
5314143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (flexibleInOtherDir) {
5324143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            updateMeasurements(); //  reset measurements
5334143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
534061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final boolean layingOutInPrimaryDirection =
535061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
536b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int count = 0;
537061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int consumedSpanCount = 0;
538b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int remainingSpan = mSpanCount;
539061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (!layingOutInPrimaryDirection) {
540061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
541061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
542061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            remainingSpan = itemSpanIndex + itemSpanSize;
543061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
544b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
545b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            int pos = layoutState.mCurrentPosition;
5469ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            final int spanSize = getSpanSize(recycler, state, pos);
54773304eff156157f62075215e795e774803a6f96aYigit Boyar            if (spanSize > mSpanCount) {
5481e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                throw new IllegalArgumentException("Item at position " + pos + " requires "
5491e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                        + spanSize + " spans but GridLayoutManager has only " + mSpanCount
55073304eff156157f62075215e795e774803a6f96aYigit Boyar                        + " spans.");
55173304eff156157f62075215e795e774803a6f96aYigit Boyar            }
552b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            remainingSpan -= spanSize;
553b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (remainingSpan < 0) {
554b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                break; // item did not fit into this row or column
555b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
556b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            View view = layoutState.next(recycler);
557b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (view == null) {
558b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                break;
559b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
560061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            consumedSpanCount += spanSize;
561b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            mSet[count] = view;
562061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            count++;
563b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
564b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
565b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (count == 0) {
566b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            result.mFinished = true;
567b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return;
568b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
569b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
570b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        int maxSize = 0;
5714143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        float maxSizeInOther = 0; // use a float to get size per span
572061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
5735e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        // we should assign spans before item decor offsets are calculated
574061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
575061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < count; i++) {
576b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            View view = mSet[i];
5779ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            if (layoutState.mScrapList == null) {
5789ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                if (layingOutInPrimaryDirection) {
5799ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addView(view);
5809ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                } else {
5819ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addView(view, 0);
5829ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                }
583b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
5849ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                if (layingOutInPrimaryDirection) {
5859ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addDisappearingView(view);
5869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                } else {
5879ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                    addDisappearingView(view, 0);
5889ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar                }
589b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
590de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            calculateItemDecorationsForChild(view, mDecorInsets);
5919ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar
592de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            measureChild(view, otherDirSpecMode, false);
593b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            final int size = mOrientationHelper.getDecoratedMeasurement(view);
594b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (size > maxSize) {
595b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                maxSize = size;
596b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
597de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
5981e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas            final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view)
5991e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                    / lp.mSpanSize;
6004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            if (otherSize > maxSizeInOther) {
6014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                maxSizeInOther = otherSize;
6024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            }
603b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
6044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (flexibleInOtherDir) {
6054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            // re-distribute columns
6064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            guessMeasurement(maxSizeInOther, currentOtherDirSize);
6074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            // now we should re-measure any item that was match parent.
6084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            maxSize = 0;
6094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            for (int i = 0; i < count; i++) {
6104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                View view = mSet[i];
611de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                measureChild(view, View.MeasureSpec.EXACTLY, true);
6124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                final int size = mOrientationHelper.getDecoratedMeasurement(view);
6134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                if (size > maxSize) {
6144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    maxSize = size;
6154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                }
6164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            }
6174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
618de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar
6194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        // Views that did not measure the maxSize has to be re-measured
6204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        // We will stop doing this once we introduce Gravity in the GLM layout params
6211e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas        for (int i = 0; i < count; i++) {
622afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            final View view = mSet[i];
623afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
6247e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
625de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                final Rect decorInsets = lp.mDecorInsets;
626de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                final int verticalInsets = decorInsets.top + decorInsets.bottom
627de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                        + lp.topMargin + lp.bottomMargin;
628de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                final int horizontalInsets = decorInsets.left + decorInsets.right
629de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                        + lp.leftMargin + lp.rightMargin;
63095017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar                final int totalSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize);
631de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                final int wSpec;
632de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                final int hSpec;
633afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                if (mOrientation == VERTICAL) {
634de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    wSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY,
635de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                            horizontalInsets, lp.width, false);
636de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    hSpec = View.MeasureSpec.makeMeasureSpec(maxSize - verticalInsets,
637de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                            View.MeasureSpec.EXACTLY);
638afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                } else {
639de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    wSpec = View.MeasureSpec.makeMeasureSpec(maxSize - horizontalInsets,
640de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                            View.MeasureSpec.EXACTLY);
641de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    hSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY,
642de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                            verticalInsets, lp.height, false);
643afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                }
644de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                measureChildWithDecorationsAndMargin(view, wSpec, hSpec, true);
645afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            }
646afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
647afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
648b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        result.mConsumed = maxSize;
649b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
6505e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        int left = 0, right = 0, top = 0, bottom = 0;
651b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (mOrientation == VERTICAL) {
652b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
653b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                bottom = layoutState.mOffset;
654b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                top = bottom - maxSize;
655b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
656b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                top = layoutState.mOffset;
657b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                bottom = top + maxSize;
658b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
659b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        } else {
660b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
661b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                right = layoutState.mOffset;
662b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                left = right - maxSize;
663b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
664b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                left = layoutState.mOffset;
665b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                right = left + maxSize;
666b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
667b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
668061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < count; i++) {
669b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            View view = mSet[i];
670b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            LayoutParams params = (LayoutParams) view.getLayoutParams();
671b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (mOrientation == VERTICAL) {
6724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                if (isLayoutRTL()) {
67395017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar                    right = getPaddingLeft() + mCachedBorders[mSpanCount - params.mSpanIndex];
6744143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
6754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                } else {
6764143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
6774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
6784143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                }
679b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            } else {
6807e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                top = getPaddingTop() + mCachedBorders[params.mSpanIndex];
681b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
682b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
683b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            // We calculate everything with View's bounding box (which includes decor and margins)
684b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            // To calculate correct layout position, we subtract margins.
6855f538711872b050b93f49a5dcaff1753e0299449Dake Gu            layoutDecoratedWithMargins(view, left, top, right, bottom);
686b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (DEBUG) {
687b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
688b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
689b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)
6905e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar                        + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize);
691b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
692b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            // Consume the available space if the view is not removed OR changed
693b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (params.isItemRemoved() || params.isItemChanged()) {
694b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                result.mIgnoreConsumed = true;
695b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
696b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri            result.mFocusable |= view.hasFocusable();
697b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
698b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        Arrays.fill(mSet, null);
699b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
700b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
7014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    /**
702de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     * Measures a child with currently known information. This is not necessarily the child's final
703de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     * measurement. (see fillChunk for details).
704de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     *
705de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     * @param view The child view to be measured
706de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     * @param otherDirParentSpecMode The RV measure spec that should be used in the secondary
707de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     *                               orientation
708de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     * @param alreadyMeasured True if we've already measured this view once
709de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar     */
710de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar    private void measureChild(View view, int otherDirParentSpecMode, boolean alreadyMeasured) {
711de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
712de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        final Rect decorInsets = lp.mDecorInsets;
713de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        final int verticalInsets = decorInsets.top + decorInsets.bottom
714de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                + lp.topMargin + lp.bottomMargin;
715de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        final int horizontalInsets = decorInsets.left + decorInsets.right
716de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                + lp.leftMargin + lp.rightMargin;
71795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        final int availableSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize);
718de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        final int wSpec;
719de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        final int hSpec;
720de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        if (mOrientation == VERTICAL) {
721de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
722de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    horizontalInsets, lp.width, false);
723de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(),
724de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    verticalInsets, lp.height, true);
725de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        } else {
726de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
727de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    verticalInsets, lp.height, false);
728de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(),
729de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar                    horizontalInsets, lp.width, true);
730de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        }
731de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar        measureChildWithDecorationsAndMargin(view, wSpec, hSpec, alreadyMeasured);
732de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar    }
733de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar
734de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar    /**
7354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * This is called after laying out a row (if vertical) or a column (if horizontal) when the
7364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * RecyclerView does not have exact measurement specs.
7374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * <p>
7384143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * Here we try to assign a best guess width or height and re-do the layout to update other
739e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas     * views that wanted to MATCH_PARENT in the non-scroll orientation.
7404143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     *
7414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @param maxSizeInOther The maximum size per span ratio from the measurement of the children.
7424143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     * @param currentOtherDirSize The size before this layout chunk. There is no reason to go below.
7434143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar     */
7444143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    private void guessMeasurement(float maxSizeInOther, int currentOtherDirSize) {
7454143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        final int contentSize = Math.round(maxSizeInOther * mSpanCount);
7464143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        // always re-calculate because borders were stretched during the fill
7474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        calculateItemBorders(Math.max(contentSize, currentOtherDirSize));
748061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
749061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
7503bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar    private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec,
751de8a5de43b647f9b61f4859a22a0ce59155f442aYigit Boyar            boolean alreadyMeasured) {
752b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
7534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        final boolean measure;
7544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (alreadyMeasured) {
7554143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp);
7564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        } else {
7574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            measure = shouldMeasureChild(child, widthSpec, heightSpec, lp);
7584143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
7594143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        if (measure) {
7604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            child.measure(widthSpec, heightSpec);
7614143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        }
762b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
763b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
7649ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar    private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
765061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int consumedSpanCount, boolean layingOutInPrimaryDirection) {
76695017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        // spans are always assigned from 0 to N no matter if it is RTL or not.
76795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        // RTL is used only when positioning the view.
76895017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        int span, start, end, diff;
7695e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        // make sure we traverse from min position to max position
7705e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        if (layingOutInPrimaryDirection) {
7715e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            start = 0;
7725e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            end = count;
7735e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            diff = 1;
7745e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        } else {
7755e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            start = count - 1;
7765e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            end = -1;
7775e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            diff = -1;
7785e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        }
77995017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar        span = 0;
7805e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        for (int i = start; i != end; i += diff) {
7815e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            View view = mSet[i];
7825e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar            LayoutParams params = (LayoutParams) view.getLayoutParams();
7839ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar            params.mSpanSize = getSpanSize(recycler, state, getPosition(view));
78495017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar            params.mSpanIndex = span;
78595017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar            span += params.mSpanSize;
7865e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar        }
7875e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar    }
7885e79cc4e2c96b0dd1d4de6f3208f9da3bcb69d26Yigit Boyar
789b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
790b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Returns the number of spans laid out by this grid.
791b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
792b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @return The number of spans
793b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @see #setSpanCount(int)
794b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
795b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public int getSpanCount() {
796b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        return mSpanCount;
797b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
798b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
799b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
800b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Sets the number of spans to be laid out.
801b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * <p>
802b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns.
803b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows.
804b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
805b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @param spanCount The total number of spans in the grid
806b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @see #getSpanCount()
807b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
808b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public void setSpanCount(int spanCount) {
809b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (spanCount == mSpanCount) {
810b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return;
811b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
812204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mPendingSpanCountChange = true;
813b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        if (spanCount < 1) {
814b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            throw new IllegalArgumentException("Span count should be at least 1. Provided "
815b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    + spanCount);
816b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
817b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        mSpanCount = spanCount;
818061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mSpanSizeLookup.invalidateSpanIndexCache();
81966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        requestLayout();
820b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
821b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
822b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
823b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * A helper class to provide the number of spans each item occupies.
824b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * <p>
825b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Default implementation sets each item to occupy exactly 1 span.
826b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     *
827b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
828b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
8291e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas    public abstract static class SpanSizeLookup {
830061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
831061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final SparseIntArray mSpanIndexCache = new SparseIntArray();
832061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
833061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        private boolean mCacheSpanIndices = false;
834061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
835b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
836b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the number of span occupied by the item at <code>position</code>.
837b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
838b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @param position The adapter position of the item
839b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The number of spans occupied by the item at the provided position
840b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
8411e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas        public abstract int getSpanSize(int position);
842b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
843b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
844061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
845061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * not. By default these values are not cached. If you are not overriding
846061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
847061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         *
848061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
849061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         */
850061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
851061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mCacheSpanIndices = cacheSpanIndices;
852061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
853061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
854061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        /**
855061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Clears the span index cache. GridLayoutManager automatically calls this method when
856061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * adapter changes occur.
857061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         */
858061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void invalidateSpanIndexCache() {
859061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanIndexCache.clear();
860061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
861061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
862061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        /**
863061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
864061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         *
865061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * @return True if results of {@link #getSpanIndex(int, int)} are cached.
866061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         */
867061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public boolean isSpanIndexCacheEnabled() {
868061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return mCacheSpanIndices;
869061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
870061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
871061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int getCachedSpanIndex(int position, int spanCount) {
872061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (!mCacheSpanIndices) {
873061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return getSpanIndex(position, spanCount);
874061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
875061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            final int existing = mSpanIndexCache.get(position, -1);
876061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (existing != -1) {
877061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return existing;
878061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
879061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            final int value = getSpanIndex(position, spanCount);
880061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanIndexCache.put(position, value);
881061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return value;
882061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
883061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
884061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        /**
885b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the final span index of the provided position.
886b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
887061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * If you have a faster way to calculate span index for your items, you should override
888061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * this method. Otherwise, you should enable span index cache
889061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
890061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * disabled, default implementation traverses all items from 0 to
891061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * <code>position</code>. When caching is enabled, it calculates from the closest cached
892061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * value before the <code>position</code>.
893061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * <p>
894061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * If you override this method, you need to make sure it is consistent with
895a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for
896061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * each item. It is called only for the reference item and rest of the items
897061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * are assigned to spans based on the reference item. For example, you cannot assign a
898061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * position to span 2 while span 1 is empty.
899b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
900061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * Note that span offsets always start with 0 and are not affected by RTL.
901b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
902061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar         * @param position  The position of the item
903b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @param spanCount The total number of spans in the grid
904b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The final span position of the item. Should be between 0 (inclusive) and
905b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <code>spanCount</code>(exclusive)
906b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
907b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanIndex(int position, int spanCount) {
908b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            int positionSpanSize = getSpanSize(position);
909b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (positionSpanSize == spanCount) {
910b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                return 0; // quick return for full-span items
911b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
91273304eff156157f62075215e795e774803a6f96aYigit Boyar            int span = 0;
913061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int startPos = 0;
914061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            // If caching is enabled, try to jump
915061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
916061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int prevKey = findReferenceIndexFromCache(position);
917061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (prevKey >= 0) {
918061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
919061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    startPos = prevKey + 1;
920061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
921061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
922061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (int i = startPos; i < position; i++) {
92373304eff156157f62075215e795e774803a6f96aYigit Boyar                int size = getSpanSize(i);
924b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                span += size;
925b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                if (span == spanCount) {
926b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    span = 0;
927b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                } else if (span > spanCount) {
928b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    // did not fit, moving to next row / column
929b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                    span = size;
930b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                }
931b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
932b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            if (span + positionSpanSize <= spanCount) {
933b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                return span;
934b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            }
935b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return 0;
936b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
937061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
938061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int findReferenceIndexFromCache(int position) {
939061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int lo = 0;
940061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int hi = mSpanIndexCache.size() - 1;
941061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
942061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            while (lo <= hi) {
943061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                final int mid = (lo + hi) >>> 1;
944061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                final int midVal = mSpanIndexCache.keyAt(mid);
945061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (midVal < position) {
946061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    lo = mid + 1;
947061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                } else {
948061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    hi = mid - 1;
949061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
950061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
951061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            int index = lo - 1;
952061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            if (index >= 0 && index < mSpanIndexCache.size()) {
953061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return mSpanIndexCache.keyAt(index);
954061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
955061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return -1;
956061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
957a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
958a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        /**
959a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * Returns the index of the group this position belongs.
960a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * <p>
961a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * For example, if grid has 3 columns and each item occupies 1 span, span group index
962a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * for item 1 will be 0, item 5 will be 1.
963a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         *
964a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * @param adapterPosition The position in adapter
965a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * @param spanCount The total number of spans in the grid
966a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         * @return The index of the span group including the item at the given adapter position
967a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar         */
968a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        public int getSpanGroupIndex(int adapterPosition, int spanCount) {
969a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int span = 0;
970a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int group = 0;
971a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            int positionSpanSize = getSpanSize(adapterPosition);
972a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            for (int i = 0; i < adapterPosition; i++) {
973a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                int size = getSpanSize(i);
974a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                span += size;
975a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (span == spanCount) {
976a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    span = 0;
977a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    group++;
978a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                } else if (span > spanCount) {
979a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    // did not fit, moving to next row / column
980a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    span = size;
981a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    group++;
982a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
983a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
984a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            if (span + positionSpanSize > spanCount) {
985a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                group++;
986a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
987a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            return group;
988a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        }
989b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
990b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
991b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    @Override
992f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar    public View onFocusSearchFailed(View focused, int focusDirection,
993f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state) {
994f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        View prevFocusedChild = findContainingItemView(focused);
995f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        if (prevFocusedChild == null) {
996f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            return null;
997f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        }
998f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        LayoutParams lp = (LayoutParams) prevFocusedChild.getLayoutParams();
999f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        final int prevSpanStart = lp.mSpanIndex;
1000f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        final int prevSpanEnd = lp.mSpanIndex + lp.mSpanSize;
1001f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        View view = super.onFocusSearchFailed(focused, focusDirection, recycler, state);
1002f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        if (view == null) {
1003f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            return null;
1004f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        }
1005f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        // LinearLayoutManager finds the last child. What we want is the child which has the same
1006f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        // spanIndex.
1007f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
1008f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        final boolean ascend = (layoutDir == LayoutState.LAYOUT_END) != mShouldReverseLayout;
1009f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        final int start, inc, limit;
1010f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        if (ascend) {
1011f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            start = getChildCount() - 1;
1012f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            inc = -1;
1013f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            limit = -1;
1014f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        } else {
1015f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            start = 0;
1016f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            inc = 1;
1017f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            limit = getChildCount();
1018f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        }
1019f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        final boolean preferLastSpan = mOrientation == VERTICAL && isLayoutRTL();
1020f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar
10219c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // The focusable candidate to be picked if no perfect focusable candidate is found.
10229c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // The best focusable candidate is the one with the highest amount of span overlap with
10239c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // the currently focused view.
10249c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        View focusableWeakCandidate = null; // somewhat matches but not strong
10259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        int focusableWeakCandidateSpanIndex = -1;
10269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        int focusableWeakCandidateOverlap = 0; // how many spans overlap
10279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri
10289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // The unfocusable candidate to become visible on the screen next, if no perfect or
10299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // weak focusable candidates are found to receive focus next.
10309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // We are only interested in partially visible unfocusable views. These are views that are
10319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // not fully visible, that is either partially overlapping, or out-of-bounds and right below
10329c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // or above RV's padded bounded area. The best unfocusable candidate is the one with the
10339c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // highest amount of span overlap with the currently focused view.
10349c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        View unfocusableWeakCandidate = null; // somewhat matches but not strong
10359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        int unfocusableWeakCandidateSpanIndex = -1;
10369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        int unfocusableWeakCandidateOverlap = 0; // how many spans overlap
10379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri
10389c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // The span group index of the start child. This indicates the span group index of the
10399c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // next focusable item to receive focus, if a focusable item within the same span group
10409c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // exists. Any focusable item beyond this group index are not relevant since they
10419c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // were already stored in the layout before onFocusSearchFailed call and were not picked
10429c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        // by the focusSearch algorithm.
10439c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        int focusableSpanGroupIndex = getSpanGroupIndex(recycler, state, start);
1044f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        for (int i = start; i != limit; i += inc) {
10459c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri            int spanGroupIndex = getSpanGroupIndex(recycler, state, i);
1046f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            View candidate = getChildAt(i);
1047f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            if (candidate == prevFocusedChild) {
1048f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                break;
1049f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            }
10509c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri
1051b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri            if (candidate.hasFocusable() && spanGroupIndex != focusableSpanGroupIndex) {
10529c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                // We are past the allowable span group index for the next focusable item.
10539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                // The search only continues if no focusable weak candidates have been found up
10549c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                // until this point, in order to find the best unfocusable candidate to become
10559c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                // visible on the screen next.
10569c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                if (focusableWeakCandidate != null) {
10579c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    break;
10589c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                }
1059f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                continue;
1060f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            }
10619c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri
1062f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            final LayoutParams candidateLp = (LayoutParams) candidate.getLayoutParams();
1063f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            final int candidateStart = candidateLp.mSpanIndex;
1064f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            final int candidateEnd = candidateLp.mSpanIndex + candidateLp.mSpanSize;
1065b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri            if (candidate.hasFocusable() && candidateStart == prevSpanStart
10669c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    && candidateEnd == prevSpanEnd) {
1067f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                return candidate; // perfect match
1068f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            }
1069f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            boolean assignAsWeek = false;
1070b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri            if ((candidate.hasFocusable() && focusableWeakCandidate == null)
1071b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri                    || (!candidate.hasFocusable() && unfocusableWeakCandidate == null)) {
1072f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                assignAsWeek = true;
1073f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            } else {
1074f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                int maxStart = Math.max(candidateStart, prevSpanStart);
1075f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                int minEnd = Math.min(candidateEnd, prevSpanEnd);
1076f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                int overlap = minEnd - maxStart;
1077b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri                if (candidate.hasFocusable()) {
10789c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    if (overlap > focusableWeakCandidateOverlap) {
10799c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                        assignAsWeek = true;
10809c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    } else if (overlap == focusableWeakCandidateOverlap
10819c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                            && preferLastSpan == (candidateStart
10829c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                            > focusableWeakCandidateSpanIndex)) {
10839c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                        assignAsWeek = true;
10849c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    }
10859c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                } else if (focusableWeakCandidate == null
10869c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                        && isViewPartiallyVisible(candidate, false, true)) {
10879c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    if (overlap > unfocusableWeakCandidateOverlap) {
10889c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                        assignAsWeek = true;
10899c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    } else if (overlap == unfocusableWeakCandidateOverlap
10909c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                            && preferLastSpan == (candidateStart
10919c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                                    > unfocusableWeakCandidateSpanIndex)) {
10929c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                        assignAsWeek = true;
10939c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    }
1094f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar                }
1095f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            }
1096f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar
1097f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            if (assignAsWeek) {
1098b174744db6a70f384d44caeb43e10854652e81b9Keyvan Amiri                if (candidate.hasFocusable()) {
10999c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    focusableWeakCandidate = candidate;
11009c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    focusableWeakCandidateSpanIndex = candidateLp.mSpanIndex;
11019c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    focusableWeakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd)
11029c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                            - Math.max(candidateStart, prevSpanStart);
11039c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                } else {
11049c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    unfocusableWeakCandidate = candidate;
11059c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    unfocusableWeakCandidateSpanIndex = candidateLp.mSpanIndex;
11069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    unfocusableWeakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd)
11079c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                            - Math.max(candidateStart, prevSpanStart);
11089c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                }
1109f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar            }
1110f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar        }
11119c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri        return (focusableWeakCandidate != null) ? focusableWeakCandidate : unfocusableWeakCandidate;
1112f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar    }
1113f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar
1114f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar    @Override
1115b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public boolean supportsPredictiveItemAnimations() {
1116204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        return mPendingSavedState == null && !mPendingSpanCountChange;
1117b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
1118b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1119b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
1120b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
1121b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
1122b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
1123061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1124b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        @Override
1125b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanSize(int position) {
1126b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return 1;
1127b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1128b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1129b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        @Override
1130b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanIndex(int position, int spanCount) {
1131b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return position % spanCount;
1132b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1133b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
1134b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1135b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    /**
1136b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     * LayoutParams used by GridLayoutManager.
113742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * <p>
113842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the
113942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is
114042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar     * expected to fill all of the space given to it.
1141b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar     */
1142b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    public static class LayoutParams extends RecyclerView.LayoutParams {
1143b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1144b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
1145b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Span Id for Views that are not laid out yet.
1146b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
1147b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public static final int INVALID_SPAN_ID = -1;
1148b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
11493a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas        int mSpanIndex = INVALID_SPAN_ID;
1150b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
11513a500f61a8bdf48904f380f2d4925fe420d18ce7Aurimas Liutikas        int mSpanSize = 0;
1152b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1153b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(Context c, AttributeSet attrs) {
1154b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(c, attrs);
1155b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1156b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1157b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(int width, int height) {
1158b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(width, height);
1159b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1160b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1161b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(ViewGroup.MarginLayoutParams source) {
1162b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(source);
1163b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1164b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1165b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(ViewGroup.LayoutParams source) {
1166b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(source);
1167b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1168b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1169b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public LayoutParams(RecyclerView.LayoutParams source) {
1170b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            super(source);
1171b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1172b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1173b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
1174b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the current span index of this View. If the View is not laid out yet, the return
1175b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * value is <code>undefined</code>.
1176b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
117795017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar         * Starting with RecyclerView <b>24.2.0</b>, span indices are always indexed from position 0
117895017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar         * even if the layout is RTL. In a vertical GridLayoutManager, <b>leftmost</b> span is span
117995017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar         * 0 if the layout is <b>LTR</b> and <b>rightmost</b> span is span 0 if the layout is
118095017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar         * <b>RTL</b>. Prior to 24.2.0, it was the opposite which was conflicting with
118195017f70973e4cbc9b7be1142e2cf887af4f4a48Yigit Boyar         * {@link SpanSizeLookup#getSpanIndex(int, int)}.
1182b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * <p>
1183b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * If the View occupies multiple spans, span with the minimum index is returned.
1184b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
1185b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The span index of the View.
1186b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
1187b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanIndex() {
1188b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return mSpanIndex;
1189b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1190b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1191b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        /**
1192b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * Returns the number of spans occupied by this View. If the View not laid out yet, the
1193b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * return value is <code>undefined</code>.
1194b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         *
1195b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         * @return The number of spans occupied by this View.
1196b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar         */
1197b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        public int getSpanSize() {
1198b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar            return mSpanSize;
1199b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar        }
1200b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar    }
1201b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar
1202b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar}
1203