StaggeredGridDefault.java revision e0e66a21916f94ebbced0d1ffe3dc652c9c7a15e
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16/**
17 * A default implementation of {@link StaggeredGrid}.
18 *
19 * This implementation tries to fill items in consecutive row order. The next
20 * item is always in same row or in the next row.
21 */
22final class StaggeredGridDefault extends StaggeredGrid {
23
24    @Override
25    public void appendItems(int toLimit) {
26        int count = mProvider.getCount();
27        int itemIndex;
28        int rowIndex;
29        if (mLocations.size() > 0) {
30            itemIndex = getLastIndex() + 1;
31            rowIndex = (mLocations.getLast().row + 1) % mNumRows;
32        } else {
33            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
34            rowIndex = mStartRow != START_DEFAULT ? mStartRow : itemIndex % mNumRows;
35        }
36
37    top_loop:
38        while (true) {
39            // find endmost row edge (.high is biggest, or .low is smallest in reversed flow)
40            int edgeRowIndex = mReversedFlow ?
41                    (mLocations.size() > 0 ? getMinLowRowIndex() : -1) :
42                    (mLocations.size() > 0 ? getMaxHighRowIndex() : -1);
43            int edge = mReversedFlow ?
44                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].low : Integer.MAX_VALUE) :
45                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].high : Integer.MIN_VALUE);
46            // fill from current row till last row so that each row will grow longer than
47            // the previous highest row.
48            for (; rowIndex < mNumRows; rowIndex++) {
49                // fill one item to a row
50                if (itemIndex == count) {
51                    break top_loop;
52                }
53                appendItemToRow(itemIndex++, rowIndex);
54                // fill more item to the row to make sure this row is longer than
55                // the previous highest row.
56                if (edgeRowIndex == -1) {
57                    edgeRowIndex = mReversedFlow ? getMinLowRowIndex() : getMaxHighRowIndex();
58                    edge = mReversedFlow ?
59                            mRows[edgeRowIndex].low :
60                            mRows[edgeRowIndex].high;
61                } else  if (rowIndex != edgeRowIndex) {
62                    while (mReversedFlow ?
63                            mRows[rowIndex].low > edge :
64                            mRows[rowIndex].high < edge) {
65                        if (itemIndex == count) {
66                            break top_loop;
67                        }
68                        appendItemToRow(itemIndex++, rowIndex);
69                    }
70                }
71            }
72            if (mReversedFlow ?
73                    mRows[getMaxLowRowIndex()].low <= toLimit :
74                    mRows[getMinHighRowIndex()].high >= toLimit) {
75                break;
76            }
77            // start fill from row 0 again
78            rowIndex = 0;
79        }
80    }
81
82    @Override
83    public void prependItems(int toLimit) {
84        if (mProvider.getCount() <= 0) return;
85        int itemIndex;
86        int rowIndex;
87        if (mLocations.size() > 0) {
88            itemIndex = getFirstIndex() - 1;
89            rowIndex = mLocations.getFirst().row;
90            if (rowIndex == 0) {
91                rowIndex = mNumRows - 1;
92            } else {
93                rowIndex--;
94            }
95        } else {
96            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
97            rowIndex = mStartRow != START_DEFAULT ? mStartRow : itemIndex % mNumRows;
98        }
99
100    top_loop:
101        while (true) {
102            // find startmost row edge (.low is smallest, or .high is biggest in reversed flow)
103            int edgeRowIndex = mReversedFlow ?
104                    (mLocations.size() > 0 ? getMaxHighRowIndex() : -1) :
105                    (mLocations.size() > 0 ? getMinLowRowIndex() : -1);
106            int edge = mReversedFlow ?
107                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].high : Integer.MIN_VALUE) :
108                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].low : Integer.MAX_VALUE);
109            for (; rowIndex >=0 ; rowIndex--) {
110                if (itemIndex < 0) {
111                    break top_loop;
112                }
113                prependItemToRow(itemIndex--, rowIndex);
114                if (edgeRowIndex == -1) {
115                    edgeRowIndex = mReversedFlow ? getMaxHighRowIndex() : getMinLowRowIndex();
116                    edge = mReversedFlow ?
117                            mRows[edgeRowIndex].high :
118                            mRows[edgeRowIndex].low;
119                } else if (rowIndex != edgeRowIndex) {
120                    while (mReversedFlow ?
121                            mRows[rowIndex].high < edge :
122                            mRows[rowIndex].low > edge) {
123                        if (itemIndex < 0) {
124                            break top_loop;
125                        }
126                        prependItemToRow(itemIndex--, rowIndex);
127                    }
128                }
129            }
130            if (mReversedFlow ?
131                    mRows[getMinHighRowIndex()].high >= toLimit :
132                    mRows[getMaxLowRowIndex()].low <= toLimit) {
133                break;
134            }
135            rowIndex = mNumRows - 1;
136        }
137    }
138
139    @Override
140    public final void stripDownTo(int itemIndex) {
141        // because we layout the items in the order that next item is either same row
142        // or next row,  so we can easily find the row range by searching items forward and
143        // backward until we see the row is 0 or mNumRow - 1
144        Location loc = getLocation(itemIndex);
145        if (loc == null) {
146            return;
147        }
148        int firstIndex = getFirstIndex();
149        int lastIndex = getLastIndex();
150        int row = loc.row;
151
152        int endIndex = itemIndex;
153        int endRow = row;
154        while (endRow < mNumRows - 1 && endIndex < lastIndex) {
155            endIndex++;
156            endRow = getLocation(endIndex).row;
157        }
158
159        int startIndex = itemIndex;
160        int startRow = row;
161        while (startRow > 0 && startIndex > firstIndex) {
162            startIndex--;
163            startRow = getLocation(startIndex).row;
164        }
165        // trim information
166        for (int i = firstIndex; i < startIndex; i++) {
167            removeFirst();
168        }
169        for (int i = endIndex; i < lastIndex; i++) {
170            removeLast();
171        }
172    }
173}
174