/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific languag`e governing permissions and * limitations under the License. */ package android.support.v7.widget; import android.content.Context; import android.graphics.Rect; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.util.AttributeSet; import android.util.Log; import android.util.SparseIntArray; import android.view.View; import android.view.ViewGroup; import java.util.Arrays; /** * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid. *
* By default, each item occupies 1 span. You can change it by providing a custom * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}. */ public class GridLayoutManager extends LinearLayoutManager { private static final boolean DEBUG = false; private static final String TAG = "GridLayoutManager"; public static final int DEFAULT_SPAN_COUNT = -1; /** * The measure spec for the scroll direction. */ static final int MAIN_DIR_SPEC = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int mSpanCount = DEFAULT_SPAN_COUNT; /** * The size of each span */ int mSizePerSpan; /** * Temporary array to keep views in layoutChunk method */ View[] mSet; final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray(); final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray(); SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup(); // re-used variable to acquire decor insets from RecyclerView final Rect mDecorInsets = new Rect(); /** * Creates a vertical GridLayoutManager * * @param context Current context, will be used to access resources. * @param spanCount The number of columns in the grid */ public GridLayoutManager(Context context, int spanCount) { super(context); setSpanCount(spanCount); } /** * @param context Current context, will be used to access resources. * @param spanCount The number of columns or rows in the grid * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link * #VERTICAL}. * @param reverseLayout When set to true, layouts from end to start. */ public GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); setSpanCount(spanCount); } /** * stackFromEnd is not supported by GridLayoutManager. Consider using * {@link #setReverseLayout(boolean)}. */ @Override public void setStackFromEnd(boolean stackFromEnd) { if (stackFromEnd) { throw new UnsupportedOperationException( "GridLayoutManager does not support stack from end." + " Consider using reverse layout"); } super.setStackFromEnd(false); } @Override public int getRowCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state) { if (mOrientation == HORIZONTAL) { return mSpanCount; } if (state.getItemCount() < 1) { return 0; } return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); } @Override public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state) { if (mOrientation == VERTICAL) { return mSpanCount; } if (state.getItemCount() < 1) { return 0; } return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); } @Override public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { ViewGroup.LayoutParams lp = host.getLayoutParams(); if (!(lp instanceof LayoutParams)) { super.onInitializeAccessibilityNodeInfoForItem(host, info); return; } LayoutParams glp = (LayoutParams) lp; int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewPosition()); if (mOrientation == HORIZONTAL) { info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( glp.getSpanIndex(), glp.getSpanSize(), spanGroupIndex, 1, mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); } else { // VERTICAL info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( spanGroupIndex , 1, glp.getSpanIndex(), glp.getSpanSize(), mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); } } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (state.isPreLayout()) { cachePreLayoutSpanMapping(); } super.onLayoutChildren(recycler, state); if (DEBUG) { validateChildOrder(); } clearPreLayoutSpanMappingCache(); } private void clearPreLayoutSpanMappingCache() { mPreLayoutSpanSizeCache.clear(); mPreLayoutSpanIndexCache.clear(); } private void cachePreLayoutSpanMapping() { final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); final int viewPosition = lp.getViewPosition(); mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize()); mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex()); } } @Override public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { mSpanSizeLookup.invalidateSpanIndexCache(); } @Override public void onItemsChanged(RecyclerView recyclerView) { mSpanSizeLookup.invalidateSpanIndexCache(); } @Override public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { mSpanSizeLookup.invalidateSpanIndexCache(); } @Override public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { mSpanSizeLookup.invalidateSpanIndexCache(); } @Override public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { mSpanSizeLookup.invalidateSpanIndexCache(); } @Override public RecyclerView.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @Override public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { return new LayoutParams(c, attrs); } @Override public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { if (lp instanceof ViewGroup.MarginLayoutParams) { return new LayoutParams((ViewGroup.MarginLayoutParams) lp); } else { return new LayoutParams(lp); } } @Override public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { return lp instanceof LayoutParams; } /** * Sets the source to get the number of spans occupied by each item in the adapter. * * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans * occupied by each item */ public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) { mSpanSizeLookup = spanSizeLookup; } /** * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager. * * @return The current {@link SpanSizeLookup} used by the GridLayoutManager. */ public SpanSizeLookup getSpanSizeLookup() { return mSpanSizeLookup; } private void updateMeasurements() { int totalSpace; if (getOrientation() == VERTICAL) { totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); } else { totalSpace = getHeight() - getPaddingBottom() - getPaddingTop(); } mSizePerSpan = totalSpace / mSpanCount; } @Override void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) { super.onAnchorReady(state, anchorInfo); updateMeasurements(); if (state.getItemCount() > 0 && !state.isPreLayout()) { ensureAnchorIsInFirstSpan(anchorInfo); } if (mSet == null || mSet.length != mSpanCount) { mSet = new View[mSpanCount]; } } private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) { int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); while (span > 0 && anchorInfo.mPosition > 0) { anchorInfo.mPosition--; span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); } } private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int viewPosition) { if (!state.isPreLayout()) { return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount); } final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); if (adapterPosition == -1) { if (DEBUG) { throw new RuntimeException("Cannot find span group index for position " + viewPosition); } Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); return 0; } return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount); } private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { if (!state.isPreLayout()) { return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount); } final int cached = mPreLayoutSpanIndexCache.get(pos, -1); if (cached != -1) { return cached; } final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); if (adapterPosition == -1) { if (DEBUG) { throw new RuntimeException("Cannot find span index for pre layout position. It is" + " not cached, not in the adapter. Pos:" + pos); } Log.w(TAG, "Cannot find span size for pre layout position. It is" + " not cached, not in the adapter. Pos:" + pos); return 0; } return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount); } private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { if (!state.isPreLayout()) { return mSpanSizeLookup.getSpanSize(pos); } final int cached = mPreLayoutSpanSizeCache.get(pos, -1); if (cached != -1) { return cached; } final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); if (adapterPosition == -1) { if (DEBUG) { throw new RuntimeException("Cannot find span size for pre layout position. It is" + " not cached, not in the adapter. Pos:" + pos); } Log.w(TAG, "Cannot find span size for pre layout position. It is" + " not cached, not in the adapter. Pos:" + pos); return 1; } return mSpanSizeLookup.getSpanSize(adapterPosition); } @Override void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { final boolean layingOutInPrimaryDirection = layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; int count = 0; int consumedSpanCount = 0; int remainingSpan = mSpanCount; if (!layingOutInPrimaryDirection) { int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); remainingSpan = itemSpanIndex + itemSpanSize; } while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { int pos = layoutState.mCurrentPosition; final int spanSize = getSpanSize(recycler, state, pos); if (spanSize > mSpanCount) { throw new IllegalArgumentException("Item at position " + pos + " requires " + spanSize + " spans but GridLayoutManager has only " + mSpanCount + " spans."); } remainingSpan -= spanSize; if (remainingSpan < 0) { break; // item did not fit into this row or column } View view = layoutState.next(recycler); if (view == null) { break; } consumedSpanCount += spanSize; mSet[count] = view; count++; } if (count == 0) { result.mFinished = true; return; } int maxSize = 0; // we should assign spans before item decor offsets are calculated assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); for (int i = 0; i < count; i++) { View view = mSet[i]; if (layoutState.mScrapList == null) { if (layingOutInPrimaryDirection) { addView(view); } else { addView(view, 0); } } else { if (layingOutInPrimaryDirection) { addDisappearingView(view); } else { addDisappearingView(view, 0); } } int spanSize = getSpanSize(recycler, state, getPosition(view)); final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize, View.MeasureSpec.EXACTLY); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (mOrientation == VERTICAL) { measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height)); } else { measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec); } final int size = mOrientationHelper.getDecoratedMeasurement(view); if (size > maxSize) { maxSize = size; } } // views that did not measure the maxSize has to be re-measured final int maxMeasureSpec = getMainDirSpec(maxSize); for (int i = 0; i < count; i ++) { final View view = mSet[i]; if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) { int spanSize = getSpanSize(recycler, state, getPosition(view)); final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize, View.MeasureSpec.EXACTLY); if (mOrientation == VERTICAL) { measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec); } else { measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec); } } } result.mConsumed = maxSize; int left = 0, right = 0, top = 0, bottom = 0; if (mOrientation == VERTICAL) { if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { bottom = layoutState.mOffset; top = bottom - maxSize; } else { top = layoutState.mOffset; bottom = top + maxSize; } } else { if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { right = layoutState.mOffset; left = right - maxSize; } else { left = layoutState.mOffset; right = left + maxSize; } } for (int i = 0; i < count; i++) { View view = mSet[i]; LayoutParams params = (LayoutParams) view.getLayoutParams(); if (mOrientation == VERTICAL) { left = getPaddingLeft() + mSizePerSpan * params.mSpanIndex; right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); } else { top = getPaddingTop() + mSizePerSpan * params.mSpanIndex; bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); } // We calculate everything with View's bounding box (which includes decor and margins) // To calculate correct layout position, we subtract margins. layoutDecorated(view, left + params.leftMargin, top + params.topMargin, right - params.rightMargin, bottom - params.bottomMargin); if (DEBUG) { Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin) + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize); } // Consume the available space if the view is not removed OR changed if (params.isItemRemoved() || params.isItemChanged()) { result.mIgnoreConsumed = true; } result.mFocusable |= view.isFocusable(); } Arrays.fill(mSet, null); } private int getMainDirSpec(int dim) { if (dim < 0) { return MAIN_DIR_SPEC; } else { return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY); } } private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) { calculateItemDecorationsForChild(child, mDecorInsets); RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left, lp.rightMargin + mDecorInsets.right); heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top, lp.bottomMargin + mDecorInsets.bottom); child.measure(widthSpec, heightSpec); } private int updateSpecWithExtra(int spec, int startInset, int endInset) { if (startInset == 0 && endInset == 0) { return spec; } final int mode = View.MeasureSpec.getMode(spec); if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) { return View.MeasureSpec.makeMeasureSpec( View.MeasureSpec.getSize(spec) - startInset - endInset, mode); } return spec; } private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, int consumedSpanCount, boolean layingOutInPrimaryDirection) { int span, spanDiff, start, end, diff; // make sure we traverse from min position to max position if (layingOutInPrimaryDirection) { start = 0; end = count; diff = 1; } else { start = count - 1; end = -1; diff = -1; } if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span span = consumedSpanCount - 1; spanDiff = -1; } else { span = 0; spanDiff = 1; } for (int i = start; i != end; i += diff) { View view = mSet[i]; LayoutParams params = (LayoutParams) view.getLayoutParams(); params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); if (spanDiff == -1 && params.mSpanSize > 1) { params.mSpanIndex = span - (params.mSpanSize - 1); } else { params.mSpanIndex = span; } span += spanDiff * params.mSpanSize; } } /** * Returns the number of spans laid out by this grid. * * @return The number of spans * @see #setSpanCount(int) */ public int getSpanCount() { return mSpanCount; } /** * Sets the number of spans to be laid out. *
* If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns. * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows. * * @param spanCount The total number of spans in the grid * @see #getSpanCount() */ public void setSpanCount(int spanCount) { if (spanCount == mSpanCount) { return; } if (spanCount < 1) { throw new IllegalArgumentException("Span count should be at least 1. Provided " + spanCount); } mSpanCount = spanCount; mSpanSizeLookup.invalidateSpanIndexCache(); } /** * A helper class to provide the number of spans each item occupies. *
* Default implementation sets each item to occupy exactly 1 span.
*
* @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
*/
public static abstract class SpanSizeLookup {
final SparseIntArray mSpanIndexCache = new SparseIntArray();
private boolean mCacheSpanIndices = false;
/**
* Returns the number of span occupied by the item at position
.
*
* @param position The adapter position of the item
* @return The number of spans occupied by the item at the provided position
*/
abstract public int getSpanSize(int position);
/**
* Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
* not. By default these values are not cached. If you are not overriding
* {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
*
* @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
*/
public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
mCacheSpanIndices = cacheSpanIndices;
}
/**
* Clears the span index cache. GridLayoutManager automatically calls this method when
* adapter changes occur.
*/
public void invalidateSpanIndexCache() {
mSpanIndexCache.clear();
}
/**
* Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
*
* @return True if results of {@link #getSpanIndex(int, int)} are cached.
*/
public boolean isSpanIndexCacheEnabled() {
return mCacheSpanIndices;
}
int getCachedSpanIndex(int position, int spanCount) {
if (!mCacheSpanIndices) {
return getSpanIndex(position, spanCount);
}
final int existing = mSpanIndexCache.get(position, -1);
if (existing != -1) {
return existing;
}
final int value = getSpanIndex(position, spanCount);
mSpanIndexCache.put(position, value);
return value;
}
/**
* Returns the final span index of the provided position.
*
* If you have a faster way to calculate span index for your items, you should override
* this method. Otherwise, you should enable span index cache
* ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
* disabled, default implementation traverses all items from 0 to
* position
. When caching is enabled, it calculates from the closest cached
* value before the position
.
*
* If you override this method, you need to make sure it is consistent with * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for * each item. It is called only for the reference item and rest of the items * are assigned to spans based on the reference item. For example, you cannot assign a * position to span 2 while span 1 is empty. *
* Note that span offsets always start with 0 and are not affected by RTL.
*
* @param position The position of the item
* @param spanCount The total number of spans in the grid
* @return The final span position of the item. Should be between 0 (inclusive) and
* spanCount
(exclusive)
*/
public int getSpanIndex(int position, int spanCount) {
int positionSpanSize = getSpanSize(position);
if (positionSpanSize == spanCount) {
return 0; // quick return for full-span items
}
int span = 0;
int startPos = 0;
// If caching is enabled, try to jump
if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
int prevKey = findReferenceIndexFromCache(position);
if (prevKey >= 0) {
span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
startPos = prevKey + 1;
}
}
for (int i = startPos; i < position; i++) {
int size = getSpanSize(i);
span += size;
if (span == spanCount) {
span = 0;
} else if (span > spanCount) {
// did not fit, moving to next row / column
span = size;
}
}
if (span + positionSpanSize <= spanCount) {
return span;
}
return 0;
}
int findReferenceIndexFromCache(int position) {
int lo = 0;
int hi = mSpanIndexCache.size() - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = mSpanIndexCache.keyAt(mid);
if (midVal < position) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
int index = lo - 1;
if (index >= 0 && index < mSpanIndexCache.size()) {
return mSpanIndexCache.keyAt(index);
}
return -1;
}
/**
* Returns the index of the group this position belongs.
*
* For example, if grid has 3 columns and each item occupies 1 span, span group index
* for item 1 will be 0, item 5 will be 1.
*
* @param adapterPosition The position in adapter
* @param spanCount The total number of spans in the grid
* @return The index of the span group including the item at the given adapter position
*/
public int getSpanGroupIndex(int adapterPosition, int spanCount) {
int span = 0;
int group = 0;
int positionSpanSize = getSpanSize(adapterPosition);
for (int i = 0; i < adapterPosition; i++) {
int size = getSpanSize(i);
span += size;
if (span == spanCount) {
span = 0;
group++;
} else if (span > spanCount) {
// did not fit, moving to next row / column
span = size;
group++;
}
}
if (span + positionSpanSize > spanCount) {
group++;
}
return group;
}
}
@Override
public boolean supportsPredictiveItemAnimations() {
return mPendingSavedState == null;
}
/**
* Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
*/
public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
@Override
public int getSpanSize(int position) {
return 1;
}
@Override
public int getSpanIndex(int position, int spanCount) {
return position % spanCount;
}
}
/**
* LayoutParams used by GridLayoutManager.
*/
public static class LayoutParams extends RecyclerView.LayoutParams {
/**
* Span Id for Views that are not laid out yet.
*/
public static final int INVALID_SPAN_ID = -1;
private int mSpanIndex = INVALID_SPAN_ID;
private int mSpanSize = 0;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(RecyclerView.LayoutParams source) {
super(source);
}
/**
* Returns the current span index of this View. If the View is not laid out yet, the return
* value is undefined
.
*
* Note that span index may change by whether the RecyclerView is RTL or not. For * example, if the number of spans is 3 and layout is RTL, the rightmost item will have * span index of 2. If the layout changes back to LTR, span index for this view will be 0. * If the item was occupying 2 spans, span indices would be 1 and 0 respectively. *
* If the View occupies multiple spans, span with the minimum index is returned.
*
* @return The span index of the View.
*/
public int getSpanIndex() {
return mSpanIndex;
}
/**
* Returns the number of spans occupied by this View. If the View not laid out yet, the
* return value is undefined
.
*
* @return The number of spans occupied by this View.
*/
public int getSpanSize() {
return mSpanSize;
}
}
}