diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:45 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:45 -0800 |
| commit | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /core/java/android/widget/GridView.java | |
| parent | 076357b8567458d4b6dfdcf839ef751634cd2bfb (diff) | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/widget/GridView.java')
| -rw-r--r-- | core/java/android/widget/GridView.java | 1842 |
1 files changed, 0 insertions, 1842 deletions
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java deleted file mode 100644 index 38bfc7c17b44..000000000000 --- a/core/java/android/widget/GridView.java +++ /dev/null @@ -1,1842 +0,0 @@ -/* - * Copyright (C) 2007 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 language governing permissions and - * limitations under the License. - */ - -package android.widget; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.SoundEffectConstants; -import android.view.animation.GridLayoutAnimationController; - - -/** - * A view that shows items in two-dimensional scrolling grid. The items in the - * grid come from the {@link ListAdapter} associated with this view. - */ -public class GridView extends AbsListView { - public static final int NO_STRETCH = 0; - public static final int STRETCH_SPACING = 1; - public static final int STRETCH_COLUMN_WIDTH = 2; - public static final int STRETCH_SPACING_UNIFORM = 3; - - public static final int AUTO_FIT = -1; - - private int mNumColumns = AUTO_FIT; - - private int mHorizontalSpacing = 0; - private int mRequestedHorizontalSpacing; - private int mVerticalSpacing = 0; - private int mStretchMode = STRETCH_COLUMN_WIDTH; - private int mColumnWidth; - private int mRequestedColumnWidth; - private int mRequestedNumColumns; - - private View mReferenceView = null; - private View mReferenceViewInSelectedRow = null; - - private int mGravity = Gravity.LEFT; - - private final Rect mTempRect = new Rect(); - - public GridView(Context context) { - super(context); - } - - public GridView(Context context, AttributeSet attrs) { - this(context, attrs, com.android.internal.R.attr.gridViewStyle); - } - - public GridView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - TypedArray a = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.GridView, defStyle, 0); - - int hSpacing = a.getDimensionPixelOffset( - com.android.internal.R.styleable.GridView_horizontalSpacing, 0); - setHorizontalSpacing(hSpacing); - - int vSpacing = a.getDimensionPixelOffset( - com.android.internal.R.styleable.GridView_verticalSpacing, 0); - setVerticalSpacing(vSpacing); - - int index = a.getInt(com.android.internal.R.styleable.GridView_stretchMode, STRETCH_COLUMN_WIDTH); - if (index >= 0) { - setStretchMode(index); - } - - int columnWidth = a.getDimensionPixelOffset(com.android.internal.R.styleable.GridView_columnWidth, -1); - if (columnWidth > 0) { - setColumnWidth(columnWidth); - } - - int numColumns = a.getInt(com.android.internal.R.styleable.GridView_numColumns, 1); - setNumColumns(numColumns); - - index = a.getInt(com.android.internal.R.styleable.GridView_gravity, -1); - if (index >= 0) { - setGravity(index); - } - - a.recycle(); - } - - @Override - public ListAdapter getAdapter() { - return mAdapter; - } - - /** - * Sets the data behind this GridView. - * - * @param adapter the adapter providing the grid's data - */ - @Override - public void setAdapter(ListAdapter adapter) { - if (null != mAdapter) { - mAdapter.unregisterDataSetObserver(mDataSetObserver); - } - - resetList(); - mRecycler.clear(); - mAdapter = adapter; - - mOldSelectedPosition = INVALID_POSITION; - mOldSelectedRowId = INVALID_ROW_ID; - - if (mAdapter != null) { - mOldItemCount = mItemCount; - mItemCount = mAdapter.getCount(); - mDataChanged = true; - checkFocus(); - - mDataSetObserver = new AdapterDataSetObserver(); - mAdapter.registerDataSetObserver(mDataSetObserver); - - mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); - - int position; - if (mStackFromBottom) { - position = lookForSelectablePosition(mItemCount - 1, false); - } else { - position = lookForSelectablePosition(0, true); - } - setSelectedPositionInt(position); - setNextSelectedPositionInt(position); - checkSelectionChanged(); - } else { - checkFocus(); - // Nothing selected - checkSelectionChanged(); - } - - requestLayout(); - } - - @Override - int lookForSelectablePosition(int position, boolean lookDown) { - final ListAdapter adapter = mAdapter; - if (adapter == null || isInTouchMode()) { - return INVALID_POSITION; - } - - if (position < 0 || position >= mItemCount) { - return INVALID_POSITION; - } - return position; - } - - /** - * {@inheritDoc} - */ - @Override - void fillGap(boolean down) { - final int numColumns = mNumColumns; - final int verticalSpacing = mVerticalSpacing; - - final int count = getChildCount(); - - if (down) { - final int startOffset = count > 0 ? - getChildAt(count - 1).getBottom() + verticalSpacing : getListPaddingTop(); - int position = mFirstPosition + count; - if (mStackFromBottom) { - position += numColumns - 1; - } - fillDown(position, startOffset); - correctTooHigh(numColumns, verticalSpacing, getChildCount()); - } else { - final int startOffset = count > 0 ? - getChildAt(0).getTop() - verticalSpacing : getHeight() - getListPaddingBottom(); - int position = mFirstPosition; - if (!mStackFromBottom) { - position -= numColumns; - } else { - position--; - } - fillUp(position, startOffset); - correctTooLow(numColumns, verticalSpacing, getChildCount()); - } - } - - /** - * Fills the list from pos down to the end of the list view. - * - * @param pos The first position to put in the list - * - * @param nextTop The location where the top of the item associated with pos - * should be drawn - * - * @return The view that is currently selected, if it happens to be in the - * range that we draw. - */ - private View fillDown(int pos, int nextTop) { - View selectedView = null; - - final int end = (mBottom - mTop) - mListPadding.bottom; - - while (nextTop < end && pos < mItemCount) { - View temp = makeRow(pos, nextTop, true); - if (temp != null) { - selectedView = temp; - } - - nextTop = mReferenceView.getBottom() + mVerticalSpacing; - - pos += mNumColumns; - } - - return selectedView; - } - - private View makeRow(int startPos, int y, boolean flow) { - final int columnWidth = mColumnWidth; - final int horizontalSpacing = mHorizontalSpacing; - - int last; - int nextLeft = mListPadding.left + ((mStretchMode == STRETCH_SPACING_UNIFORM) ? horizontalSpacing : 0); - - if (!mStackFromBottom) { - last = Math.min(startPos + mNumColumns, mItemCount); - } else { - last = startPos + 1; - startPos = Math.max(0, startPos - mNumColumns + 1); - - if (last - startPos < mNumColumns) { - nextLeft += (mNumColumns - (last - startPos)) * (columnWidth + horizontalSpacing); - } - } - - View selectedView = null; - - final boolean hasFocus = shouldShowSelector(); - final boolean inClick = touchModeDrawsInPressedState(); - final int selectedPosition = mSelectedPosition; - - mReferenceView = null; - - for (int pos = startPos; pos < last; pos++) { - // is this the selected item? - boolean selected = pos == selectedPosition; - // does the list view have focus or contain focus - - final int where = flow ? -1 : pos - startPos; - final View child = makeAndAddView(pos, y, flow, nextLeft, selected, where); - mReferenceView = child; - - nextLeft += columnWidth; - if (pos < last - 1) { - nextLeft += horizontalSpacing; - } - - if (selected && (hasFocus || inClick)) { - selectedView = child; - } - } - - if (selectedView != null) { - mReferenceViewInSelectedRow = mReferenceView; - } - - return selectedView; - } - - /** - * Fills the list from pos up to the top of the list view. - * - * @param pos The first position to put in the list - * - * @param nextBottom The location where the bottom of the item associated - * with pos should be drawn - * - * @return The view that is currently selected - */ - private View fillUp(int pos, int nextBottom) { - View selectedView = null; - - final int end = mListPadding.top; - - while (nextBottom > end && pos >= 0) { - - View temp = makeRow(pos, nextBottom, false); - if (temp != null) { - selectedView = temp; - } - - nextBottom = mReferenceView.getTop() - mVerticalSpacing; - - mFirstPosition = pos; - - pos -= mNumColumns; - } - - if (mStackFromBottom) { - mFirstPosition = Math.max(0, pos + 1); - } - - return selectedView; - } - - /** - * Fills the list from top to bottom, starting with mFirstPosition - * - * @param nextTop The location where the top of the first item should be - * drawn - * - * @return The view that is currently selected - */ - private View fillFromTop(int nextTop) { - mFirstPosition = Math.min(mFirstPosition, mSelectedPosition); - mFirstPosition = Math.min(mFirstPosition, mItemCount - 1); - if (mFirstPosition < 0) { - mFirstPosition = 0; - } - mFirstPosition -= mFirstPosition % mNumColumns; - return fillDown(mFirstPosition, nextTop); - } - - private View fillFromBottom(int lastPosition, int nextBottom) { - lastPosition = Math.max(lastPosition, mSelectedPosition); - lastPosition = Math.min(lastPosition, mItemCount - 1); - - final int invertedPosition = mItemCount - 1 - lastPosition; - lastPosition = mItemCount - 1 - (invertedPosition - (invertedPosition % mNumColumns)); - - return fillUp(lastPosition, nextBottom); - } - - private View fillSelection(int childrenTop, int childrenBottom) { - final int selectedPosition = reconcileSelectedPosition(); - final int numColumns = mNumColumns; - final int verticalSpacing = mVerticalSpacing; - - int rowStart; - int rowEnd = -1; - - if (!mStackFromBottom) { - rowStart = selectedPosition - (selectedPosition % numColumns); - } else { - final int invertedSelection = mItemCount - 1 - selectedPosition; - - rowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns)); - rowStart = Math.max(0, rowEnd - numColumns + 1); - } - - final int fadingEdgeLength = getVerticalFadingEdgeLength(); - final int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength, rowStart); - - final View sel = makeRow(mStackFromBottom ? rowEnd : rowStart, topSelectionPixel, true); - mFirstPosition = rowStart; - - final View referenceView = mReferenceView; - - if (!mStackFromBottom) { - fillDown(rowStart + numColumns, referenceView.getBottom() + verticalSpacing); - pinToBottom(childrenBottom); - fillUp(rowStart - numColumns, referenceView.getTop() - verticalSpacing); - adjustViewsUpOrDown(); - } else { - final int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, - fadingEdgeLength, numColumns, rowStart); - final int offset = bottomSelectionPixel - referenceView.getBottom(); - offsetChildrenTopAndBottom(offset); - fillUp(rowStart - 1, referenceView.getTop() - verticalSpacing); - pinToTop(childrenTop); - fillDown(rowEnd + numColumns, referenceView.getBottom() + verticalSpacing); - adjustViewsUpOrDown(); - } - - return sel; - } - - private void pinToTop(int childrenTop) { - if (mFirstPosition == 0) { - final int top = getChildAt(0).getTop(); - final int offset = childrenTop - top; - if (offset < 0) { - offsetChildrenTopAndBottom(offset); - } - } - } - - private void pinToBottom(int childrenBottom) { - final int count = getChildCount(); - if (mFirstPosition + count == mItemCount) { - final int bottom = getChildAt(count - 1).getBottom(); - final int offset = childrenBottom - bottom; - if (offset > 0) { - offsetChildrenTopAndBottom(offset); - } - } - } - - @Override - int findMotionRow(int y) { - final int childCount = getChildCount(); - if (childCount > 0) { - - final int numColumns = mNumColumns; - if (!mStackFromBottom) { - for (int i = 0; i < childCount; i += numColumns) { - if (y <= getChildAt(i).getBottom()) { - return mFirstPosition + i; - } - } - } else { - for (int i = childCount - 1; i >= 0; i -= numColumns) { - if (y >= getChildAt(i).getTop()) { - return mFirstPosition + i; - } - } - } - - return mFirstPosition + childCount - 1; - } - return INVALID_POSITION; - } - - /** - * Layout during a scroll that results from tracking motion events. Places - * the mMotionPosition view at the offset specified by mMotionViewTop, and - * then build surrounding views from there. - * - * @param position the position at which to start filling - * @param top the top of the view at that position - * @return The selected view, or null if the selected view is outside the - * visible area. - */ - private View fillSpecific(int position, int top) { - final int numColumns = mNumColumns; - - int motionRowStart; - int motionRowEnd = -1; - - if (!mStackFromBottom) { - motionRowStart = position - (position % numColumns); - } else { - final int invertedSelection = mItemCount - 1 - position; - - motionRowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns)); - motionRowStart = Math.max(0, motionRowEnd - numColumns + 1); - } - - final View temp = makeRow(mStackFromBottom ? motionRowEnd : motionRowStart, top, true); - - // Possibly changed again in fillUp if we add rows above this one. - mFirstPosition = motionRowStart; - - final View referenceView = mReferenceView; - final int verticalSpacing = mVerticalSpacing; - - View above; - View below; - - if (!mStackFromBottom) { - above = fillUp(motionRowStart - numColumns, referenceView.getTop() - verticalSpacing); - adjustViewsUpOrDown(); - below = fillDown(motionRowStart + numColumns, referenceView.getBottom() + verticalSpacing); - // Check if we have dragged the bottom of the grid too high - final int childCount = getChildCount(); - if (childCount > 0) { - correctTooHigh(numColumns, verticalSpacing, childCount); - } - } else { - below = fillDown(motionRowEnd + numColumns, referenceView.getBottom() + verticalSpacing); - adjustViewsUpOrDown(); - above = fillUp(motionRowStart - 1, referenceView.getTop() - verticalSpacing); - // Check if we have dragged the bottom of the grid too high - final int childCount = getChildCount(); - if (childCount > 0) { - correctTooLow(numColumns, verticalSpacing, childCount); - } - } - - if (temp != null) { - return temp; - } else if (above != null) { - return above; - } else { - return below; - } - } - - private void correctTooHigh(int numColumns, int verticalSpacing, int childCount) { - // First see if the last item is visible - final int lastPosition = mFirstPosition + childCount - 1; - if (lastPosition == mItemCount - 1 && childCount > 0) { - // Get the last child ... - final View lastChild = getChildAt(childCount - 1); - - // ... and its bottom edge - final int lastBottom = lastChild.getBottom(); - // This is bottom of our drawable area - final int end = (mBottom - mTop) - mListPadding.bottom; - - // This is how far the bottom edge of the last view is from the bottom of the - // drawable area - int bottomOffset = end - lastBottom; - - final View firstChild = getChildAt(0); - final int firstTop = firstChild.getTop(); - - // Make sure we are 1) Too high, and 2) Either there are more rows above the - // first row or the first row is scrolled off the top of the drawable area - if (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < mListPadding.top)) { - if (mFirstPosition == 0) { - // Don't pull the top too far down - bottomOffset = Math.min(bottomOffset, mListPadding.top - firstTop); - } - - // Move everything down - offsetChildrenTopAndBottom(bottomOffset); - if (mFirstPosition > 0) { - // Fill the gap that was opened above mFirstPosition with more rows, if - // possible - fillUp(mFirstPosition - (mStackFromBottom ? 1 : numColumns), - firstChild.getTop() - verticalSpacing); - // Close up the remaining gap - adjustViewsUpOrDown(); - } - } - } - } - - private void correctTooLow(int numColumns, int verticalSpacing, int childCount) { - if (mFirstPosition == 0 && childCount > 0) { - // Get the first child ... - final View firstChild = getChildAt(0); - - // ... and its top edge - final int firstTop = firstChild.getTop(); - - // This is top of our drawable area - final int start = mListPadding.top; - - // This is bottom of our drawable area - final int end = (mBottom - mTop) - mListPadding.bottom; - - // This is how far the top edge of the first view is from the top of the - // drawable area - int topOffset = firstTop - start; - final View lastChild = getChildAt(childCount - 1); - final int lastBottom = lastChild.getBottom(); - final int lastPosition = mFirstPosition + childCount - 1; - - // Make sure we are 1) Too low, and 2) Either there are more rows below the - // last row or the last row is scrolled off the bottom of the drawable area - if (topOffset > 0 && (lastPosition < mItemCount - 1 || lastBottom > end)) { - if (lastPosition == mItemCount - 1 ) { - // Don't pull the bottom too far up - topOffset = Math.min(topOffset, lastBottom - end); - } - - // Move everything up - offsetChildrenTopAndBottom(-topOffset); - if (lastPosition < mItemCount - 1) { - // Fill the gap that was opened below the last position with more rows, if - // possible - fillDown(lastPosition + (!mStackFromBottom ? 1 : numColumns), - lastChild.getBottom() + verticalSpacing); - // Close up the remaining gap - adjustViewsUpOrDown(); - } - } - } - } - - /** - * Fills the grid based on positioning the new selection at a specific - * location. The selection may be moved so that it does not intersect the - * faded edges. The grid is then filled upwards and downwards from there. - * - * @param selectedTop Where the selected item should be - * @param childrenTop Where to start drawing children - * @param childrenBottom Last pixel where children can be drawn - * @return The view that currently has selection - */ - private View fillFromSelection(int selectedTop, int childrenTop, int childrenBottom) { - final int fadingEdgeLength = getVerticalFadingEdgeLength(); - final int selectedPosition = mSelectedPosition; - final int numColumns = mNumColumns; - final int verticalSpacing = mVerticalSpacing; - - int rowStart; - int rowEnd = -1; - - if (!mStackFromBottom) { - rowStart = selectedPosition - (selectedPosition % numColumns); - } else { - int invertedSelection = mItemCount - 1 - selectedPosition; - - rowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns)); - rowStart = Math.max(0, rowEnd - numColumns + 1); - } - - View sel; - View referenceView; - - int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength, rowStart); - int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, fadingEdgeLength, - numColumns, rowStart); - - sel = makeRow(mStackFromBottom ? rowEnd : rowStart, selectedTop, true); - // Possibly changed again in fillUp if we add rows above this one. - mFirstPosition = rowStart; - - referenceView = mReferenceView; - adjustForTopFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel); - adjustForBottomFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel); - - if (!mStackFromBottom) { - fillUp(rowStart - numColumns, referenceView.getTop() - verticalSpacing); - adjustViewsUpOrDown(); - fillDown(rowStart + numColumns, referenceView.getBottom() + verticalSpacing); - } else { - fillDown(rowEnd + numColumns, referenceView.getBottom() + verticalSpacing); - adjustViewsUpOrDown(); - fillUp(rowStart - 1, referenceView.getTop() - verticalSpacing); - } - - - return sel; - } - - /** - * Calculate the bottom-most pixel we can draw the selection into - * - * @param childrenBottom Bottom pixel were children can be drawn - * @param fadingEdgeLength Length of the fading edge in pixels, if present - * @param numColumns Number of columns in the grid - * @param rowStart The start of the row that will contain the selection - * @return The bottom-most pixel we can draw the selection into - */ - private int getBottomSelectionPixel(int childrenBottom, int fadingEdgeLength, - int numColumns, int rowStart) { - // Last pixel we can draw the selection into - int bottomSelectionPixel = childrenBottom; - if (rowStart + numColumns - 1 < mItemCount - 1) { - bottomSelectionPixel -= fadingEdgeLength; - } - return bottomSelectionPixel; - } - - /** - * Calculate the top-most pixel we can draw the selection into - * - * @param childrenTop Top pixel were children can be drawn - * @param fadingEdgeLength Length of the fading edge in pixels, if present - * @param rowStart The start of the row that will contain the selection - * @return The top-most pixel we can draw the selection into - */ - private int getTopSelectionPixel(int childrenTop, int fadingEdgeLength, int rowStart) { - // first pixel we can draw the selection into - int topSelectionPixel = childrenTop; - if (rowStart > 0) { - topSelectionPixel += fadingEdgeLength; - } - return topSelectionPixel; - } - - /** - * Move all views upwards so the selected row does not interesect the bottom - * fading edge (if necessary). - * - * @param childInSelectedRow A child in the row that contains the selection - * @param topSelectionPixel The topmost pixel we can draw the selection into - * @param bottomSelectionPixel The bottommost pixel we can draw the - * selection into - */ - private void adjustForBottomFadingEdge(View childInSelectedRow, - int topSelectionPixel, int bottomSelectionPixel) { - // Some of the newly selected item extends below the bottom of the - // list - if (childInSelectedRow.getBottom() > bottomSelectionPixel) { - - // Find space available above the selection into which we can - // scroll upwards - int spaceAbove = childInSelectedRow.getTop() - topSelectionPixel; - - // Find space required to bring the bottom of the selected item - // fully into view - int spaceBelow = childInSelectedRow.getBottom() - bottomSelectionPixel; - int offset = Math.min(spaceAbove, spaceBelow); - - // Now offset the selected item to get it into view - offsetChildrenTopAndBottom(-offset); - } - } - - /** - * Move all views upwards so the selected row does not interesect the top - * fading edge (if necessary). - * - * @param childInSelectedRow A child in the row that contains the selection - * @param topSelectionPixel The topmost pixel we can draw the selection into - * @param bottomSelectionPixel The bottommost pixel we can draw the - * selection into - */ - private void adjustForTopFadingEdge(View childInSelectedRow, - int topSelectionPixel, int bottomSelectionPixel) { - // Some of the newly selected item extends above the top of the list - if (childInSelectedRow.getTop() < topSelectionPixel) { - // Find space required to bring the top of the selected item - // fully into view - int spaceAbove = topSelectionPixel - childInSelectedRow.getTop(); - - // Find space available below the selection into which we can - // scroll downwards - int spaceBelow = bottomSelectionPixel - childInSelectedRow.getBottom(); - int offset = Math.min(spaceAbove, spaceBelow); - - // Now offset the selected item to get it into view - offsetChildrenTopAndBottom(offset); - } - } - - /** - * Fills the grid based on positioning the new selection relative to the old - * selection. The new selection will be placed at, above, or below the - * location of the new selection depending on how the selection is moving. - * The selection will then be pinned to the visible part of the screen, - * excluding the edges that are faded. The grid is then filled upwards and - * downwards from there. - * - * @param delta Which way we are moving - * @param childrenTop Where to start drawing children - * @param childrenBottom Last pixel where children can be drawn - * @return The view that currently has selection - */ - private View moveSelection(int delta, int childrenTop, int childrenBottom) { - final int fadingEdgeLength = getVerticalFadingEdgeLength(); - final int selectedPosition = mSelectedPosition; - final int numColumns = mNumColumns; - final int verticalSpacing = mVerticalSpacing; - - int oldRowStart; - int rowStart; - int rowEnd = -1; - - if (!mStackFromBottom) { - oldRowStart = (selectedPosition - delta) - ((selectedPosition - delta) % numColumns); - - rowStart = selectedPosition - (selectedPosition % numColumns); - } else { - int invertedSelection = mItemCount - 1 - selectedPosition; - - rowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns)); - rowStart = Math.max(0, rowEnd - numColumns + 1); - - invertedSelection = mItemCount - 1 - (selectedPosition - delta); - oldRowStart = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns)); - oldRowStart = Math.max(0, oldRowStart - numColumns + 1); - } - - final int rowDelta = rowStart - oldRowStart; - - final int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength, rowStart); - final int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, fadingEdgeLength, - numColumns, rowStart); - - // Possibly changed again in fillUp if we add rows above this one. - mFirstPosition = rowStart; - - View sel; - View referenceView; - - if (rowDelta > 0) { - /* - * Case 1: Scrolling down. - */ - - final int oldBottom = mReferenceViewInSelectedRow == null ? 0 : - mReferenceViewInSelectedRow.getBottom(); - - sel = makeRow(mStackFromBottom ? rowEnd : rowStart, oldBottom + verticalSpacing, true); - referenceView = mReferenceView; - - adjustForBottomFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel); - } else if (rowDelta < 0) { - /* - * Case 2: Scrolling up. - */ - final int oldTop = mReferenceViewInSelectedRow == null ? - 0 : mReferenceViewInSelectedRow .getTop(); - - sel = makeRow(mStackFromBottom ? rowEnd : rowStart, oldTop - verticalSpacing, false); - referenceView = mReferenceView; - - adjustForTopFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel); - } else { - /* - * Keep selection where it was - */ - final int oldTop = mReferenceViewInSelectedRow == null ? - 0 : mReferenceViewInSelectedRow .getTop(); - - sel = makeRow(mStackFromBottom ? rowEnd : rowStart, oldTop, true); - referenceView = mReferenceView; - } - - if (!mStackFromBottom) { - fillUp(rowStart - numColumns, referenceView.getTop() - verticalSpacing); - adjustViewsUpOrDown(); - fillDown(rowStart + numColumns, referenceView.getBottom() + verticalSpacing); - } else { - fillDown(rowEnd + numColumns, referenceView.getBottom() + verticalSpacing); - adjustViewsUpOrDown(); - fillUp(rowStart - 1, referenceView.getTop() - verticalSpacing); - } - - return sel; - } - - private void determineColumns(int availableSpace) { - final int requestedHorizontalSpacing = mRequestedHorizontalSpacing; - final int stretchMode = mStretchMode; - final int requestedColumnWidth = mRequestedColumnWidth; - - if (mRequestedNumColumns == AUTO_FIT) { - if (requestedColumnWidth > 0) { - // Client told us to pick the number of columns - mNumColumns = (availableSpace + requestedHorizontalSpacing) / - (requestedColumnWidth + requestedHorizontalSpacing); - } else { - // Just make up a number if we don't have enough info - mNumColumns = 2; - } - } else { - // We picked the columns - mNumColumns = mRequestedNumColumns; - } - - if (mNumColumns <= 0) { - mNumColumns = 1; - } - - switch (stretchMode) { - case NO_STRETCH: - // Nobody stretches - mColumnWidth = requestedColumnWidth; - mHorizontalSpacing = requestedHorizontalSpacing; - break; - - default: - int spaceLeftOver = availableSpace - (mNumColumns * requestedColumnWidth) - - ((mNumColumns - 1) * requestedHorizontalSpacing); - switch (stretchMode) { - case STRETCH_COLUMN_WIDTH: - // Stretch the columns - mColumnWidth = requestedColumnWidth + spaceLeftOver / mNumColumns; - mHorizontalSpacing = requestedHorizontalSpacing; - break; - - case STRETCH_SPACING: - // Stretch the spacing between columns - mColumnWidth = requestedColumnWidth; - if (mNumColumns > 1) { - mHorizontalSpacing = requestedHorizontalSpacing + - spaceLeftOver / (mNumColumns - 1); - } else { - mHorizontalSpacing = requestedHorizontalSpacing + spaceLeftOver; - } - break; - - case STRETCH_SPACING_UNIFORM: - // Stretch the spacing between columns - mColumnWidth = requestedColumnWidth; - if (mNumColumns > 1) { - mHorizontalSpacing = requestedHorizontalSpacing + - spaceLeftOver / (mNumColumns + 1); - } else { - mHorizontalSpacing = requestedHorizontalSpacing + spaceLeftOver; - } - break; - } - - break; - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Sets up mListPadding - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthMode == MeasureSpec.UNSPECIFIED) { - if (mColumnWidth > 0) { - widthSize = mColumnWidth + mListPadding.left + mListPadding.right; - } else { - widthSize = mListPadding.left + mListPadding.right; - } - widthSize += getVerticalScrollbarWidth(); - } - - int childWidth = widthSize - mListPadding.left - mListPadding.right; - determineColumns(childWidth); - - int childHeight = 0; - - mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); - final int count = mItemCount; - if (count > 0) { - final View child = obtainView(0); - final int childViewType = mAdapter.getItemViewType(0); - - AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); - if (lp == null) { - lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); - child.setLayoutParams(lp); - } - lp.viewType = childViewType; - - final int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, - mListPadding.left + mListPadding.right, lp.width); - - int lpHeight = lp.height; - - int childHeightSpec; - if (lpHeight > 0) { - childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); - } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - } - - child.measure(childWidthSpec, childHeightSpec); - childHeight = child.getMeasuredHeight(); - - if (mRecycler.shouldRecycleViewType(childViewType)) { - mRecycler.addScrapView(child); - } - } - - if (heightMode == MeasureSpec.UNSPECIFIED) { - heightSize = mListPadding.top + mListPadding.bottom + childHeight + - getVerticalFadingEdgeLength() * 2; - } - - if (heightMode == MeasureSpec.AT_MOST) { - int ourSize = mListPadding.top + mListPadding.bottom; - - final int numColumns = mNumColumns; - for (int i = 0; i < count; i += numColumns) { - ourSize += childHeight; - if (i + numColumns < count) { - ourSize += mVerticalSpacing; - } - if (ourSize >= heightSize) { - ourSize = heightSize; - break; - } - } - heightSize = ourSize; - } - - setMeasuredDimension(widthSize, heightSize); - mWidthMeasureSpec = widthMeasureSpec; - } - - @Override - protected void attachLayoutAnimationParameters(View child, - ViewGroup.LayoutParams params, int index, int count) { - - GridLayoutAnimationController.AnimationParameters animationParams = - (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters; - - if (animationParams == null) { - animationParams = new GridLayoutAnimationController.AnimationParameters(); - params.layoutAnimationParameters = animationParams; - } - - animationParams.count = count; - animationParams.index = index; - animationParams.columnsCount = mNumColumns; - animationParams.rowsCount = count / mNumColumns; - - if (!mStackFromBottom) { - animationParams.column = index % mNumColumns; - animationParams.row = index / mNumColumns; - } else { - final int invertedIndex = count - 1 - index; - - animationParams.column = mNumColumns - 1 - (invertedIndex % mNumColumns); - animationParams.row = animationParams.rowsCount - 1 - invertedIndex / mNumColumns; - } - } - - @Override - protected void layoutChildren() { - final boolean blockLayoutRequests = mBlockLayoutRequests; - if (!blockLayoutRequests) { - mBlockLayoutRequests = true; - } - - try { - super.layoutChildren(); - - invalidate(); - - if (mAdapter == null) { - resetList(); - invokeOnItemScrollListener(); - return; - } - - final int childrenTop = mListPadding.top; - final int childrenBottom = mBottom - mTop - mListPadding.bottom; - - int childCount = getChildCount(); - int index; - int delta = 0; - - View sel; - View oldSel = null; - View oldFirst = null; - View newSel = null; - - // Remember stuff we will need down below - switch (mLayoutMode) { - case LAYOUT_SET_SELECTION: - index = mNextSelectedPosition - mFirstPosition; - if (index >= 0 && index < childCount) { - newSel = getChildAt(index); - } - break; - case LAYOUT_FORCE_TOP: - case LAYOUT_FORCE_BOTTOM: - case LAYOUT_SPECIFIC: - case LAYOUT_SYNC: - break; - case LAYOUT_MOVE_SELECTION: - if (mNextSelectedPosition >= 0) { - delta = mNextSelectedPosition - mSelectedPosition; - } - break; - default: - // Remember the previously selected view - index = mSelectedPosition - mFirstPosition; - if (index >= 0 && index < childCount) { - oldSel = getChildAt(index); - } - - // Remember the previous first child - oldFirst = getChildAt(0); - } - - boolean dataChanged = mDataChanged; - if (dataChanged) { - handleDataChanged(); - } - - // Handle the empty set by removing all views that are visible - // and calling it a day - if (mItemCount == 0) { - resetList(); - invokeOnItemScrollListener(); - return; - } - - setSelectedPositionInt(mNextSelectedPosition); - - // Pull all children into the RecycleBin. - // These views will be reused if possible - final int firstPosition = mFirstPosition; - final RecycleBin recycleBin = mRecycler; - - if (dataChanged) { - for (int i = 0; i < childCount; i++) { - recycleBin.addScrapView(getChildAt(i)); - } - } else { - recycleBin.fillActiveViews(childCount, firstPosition); - } - - // Clear out old views - //removeAllViewsInLayout(); - detachAllViewsFromParent(); - - switch (mLayoutMode) { - case LAYOUT_SET_SELECTION: - if (newSel != null) { - sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom); - } else { - sel = fillSelection(childrenTop, childrenBottom); - } - break; - case LAYOUT_FORCE_TOP: - mFirstPosition = 0; - sel = fillFromTop(childrenTop); - adjustViewsUpOrDown(); - break; - case LAYOUT_FORCE_BOTTOM: - sel = fillUp(mItemCount - 1, childrenBottom); - adjustViewsUpOrDown(); - break; - case LAYOUT_SPECIFIC: - sel = fillSpecific(mSelectedPosition, mSpecificTop); - break; - case LAYOUT_SYNC: - sel = fillSpecific(mSyncPosition, mSpecificTop); - break; - case LAYOUT_MOVE_SELECTION: - // Move the selection relative to its old position - sel = moveSelection(delta, childrenTop, childrenBottom); - break; - default: - if (childCount == 0) { - if (!mStackFromBottom) { - setSelectedPositionInt(0); - sel = fillFromTop(childrenTop); - } else { - final int last = mItemCount - 1; - setSelectedPositionInt(last); - sel = fillFromBottom(last, childrenBottom); - } - } else { - if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) { - sel = fillSpecific(mSelectedPosition, oldSel == null ? - childrenTop : oldSel.getTop()); - } else if (mFirstPosition < mItemCount) { - sel = fillSpecific(mFirstPosition, oldFirst == null ? - childrenTop : oldFirst.getTop()); - } else { - sel = fillSpecific(0, childrenTop); - } - } - break; - } - - // Flush any cached views that did not get reused above - recycleBin.scrapActiveViews(); - - if (sel != null) { - positionSelector(sel); - mSelectedTop = sel.getTop(); - } else { - mSelectedTop = 0; - mSelectorRect.setEmpty(); - } - - mLayoutMode = LAYOUT_NORMAL; - mDataChanged = false; - mNeedSync = false; - setNextSelectedPositionInt(mSelectedPosition); - - updateScrollIndicators(); - - if (mItemCount > 0) { - checkSelectionChanged(); - } - - invokeOnItemScrollListener(); - } finally { - if (!blockLayoutRequests) { - mBlockLayoutRequests = false; - } - } - } - - - /** - * Obtain the view and add it to our list of children. The view can be made - * fresh, converted from an unused view, or used as is if it was in the - * recycle bin. - * - * @param position Logical position in the list - * @param y Top or bottom edge of the view to add - * @param flow if true, align top edge to y. If false, align bottom edge to - * y. - * @param childrenLeft Left edge where children should be positioned - * @param selected Is this position selected? - * @param where to add new item in the list - * @return View that was added - */ - private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, - boolean selected, int where) { - View child; - - if (!mDataChanged) { - // Try to use an exsiting view for this position - child = mRecycler.getActiveView(position); - if (child != null) { - // Found it -- we're using an existing child - // This just needs to be positioned - setupChild(child, position, y, flow, childrenLeft, selected, true, where); - return child; - } - } - - // Make a new view for this position, or convert an unused view if - // possible - child = obtainView(position); - - // This needs to be positioned and measured - setupChild(child, position, y, flow, childrenLeft, selected, false, where); - - return child; - } - - /** - * Add a view as a child and make sure it is measured (if necessary) and - * positioned properly. - * - * @param child The view to add - * @param position The position of the view - * @param y The y position relative to which this view will be positioned - * @param flow if true, align top edge to y. If false, align bottom edge - * to y. - * @param childrenLeft Left edge where children should be positioned - * @param selected Is this position selected? - * @param recycled Has this view been pulled from the recycle bin? If so it - * does not need to be remeasured. - * @param where Where to add the item in the list - * - */ - private void setupChild(View child, int position, int y, boolean flow, int childrenLeft, - boolean selected, boolean recycled, int where) { - boolean isSelected = selected && shouldShowSelector(); - - final boolean updateChildSelected = isSelected != child.isSelected(); - boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested(); - - // Respect layout params that are already in the view. Otherwise make - // some up... - AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); - if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); - } - p.viewType = mAdapter.getItemViewType(position); - - if (recycled) { - attachViewToParent(child, where, p); - } else { - addViewInLayout(child, where, p, true); - } - - if (updateChildSelected) { - child.setSelected(isSelected); - if (isSelected) { - requestFocus(); - } - } - - if (needToMeasure) { - int childHeightSpec = ViewGroup.getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height); - - int childWidthSpec = ViewGroup.getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width); - child.measure(childWidthSpec, childHeightSpec); - } else { - cleanupLayoutState(child); - } - - final int w = child.getMeasuredWidth(); - final int h = child.getMeasuredHeight(); - - int childLeft; - final int childTop = flow ? y : y - h; - - switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - childLeft = childrenLeft; - break; - case Gravity.CENTER_HORIZONTAL: - childLeft = childrenLeft + ((mColumnWidth - w) / 2); - break; - case Gravity.RIGHT: - childLeft = childrenLeft + mColumnWidth - w; - break; - default: - childLeft = childrenLeft; - break; - } - - if (needToMeasure) { - final int childRight = childLeft + w; - final int childBottom = childTop + h; - child.layout(childLeft, childTop, childRight, childBottom); - } else { - child.offsetLeftAndRight(childLeft - child.getLeft()); - child.offsetTopAndBottom(childTop - child.getTop()); - } - - if (mCachingStarted) { - child.setDrawingCacheEnabled(true); - } - } - - /** - * Sets the currently selected item - * - * @param position Index (starting at 0) of the data item to be selected. - * - * If in touch mode, the item will not be selected but it will still be positioned - * appropriately. - */ - @Override - public void setSelection(int position) { - if (!isInTouchMode()) { - setNextSelectedPositionInt(position); - } else { - mResurrectToPosition = position; - } - mLayoutMode = LAYOUT_SET_SELECTION; - requestLayout(); - } - - /** - * Makes the item at the supplied position selected. - * - * @param position the position of the new selection - */ - @Override - void setSelectionInt(int position) { - mBlockLayoutRequests = true; - setNextSelectedPositionInt(position); - layoutChildren(); - - mBlockLayoutRequests = false; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return commonKey(keyCode, 1, event); - } - - @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { - return commonKey(keyCode, repeatCount, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return commonKey(keyCode, 1, event); - } - - private boolean commonKey(int keyCode, int count, KeyEvent event) { - if (mAdapter == null) { - return false; - } - - if (mDataChanged) { - layoutChildren(); - } - - boolean handled = false; - int action = event.getAction(); - - if (action != KeyEvent.ACTION_UP) { - if (mSelectedPosition < 0) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_SPACE: - case KeyEvent.KEYCODE_ENTER: - resurrectSelection(); - return true; - } - } - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - handled = arrowScroll(FOCUS_LEFT); - break; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - handled = arrowScroll(FOCUS_RIGHT); - break; - - case KeyEvent.KEYCODE_DPAD_UP: - if (!event.isAltPressed()) { - handled = arrowScroll(FOCUS_UP); - - } else { - handled = fullScroll(FOCUS_UP); - } - break; - - case KeyEvent.KEYCODE_DPAD_DOWN: - if (!event.isAltPressed()) { - handled = arrowScroll(FOCUS_DOWN); - } else { - handled = fullScroll(FOCUS_DOWN); - } - break; - - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: { - if (getChildCount() > 0 && event.getRepeatCount() == 0) { - keyPressed(); - } - - return true; - } - - case KeyEvent.KEYCODE_SPACE: - if (mPopup == null || !mPopup.isShowing()) { - if (!event.isShiftPressed()) { - handled = pageScroll(FOCUS_DOWN); - } else { - handled = pageScroll(FOCUS_UP); - } - } - break; - } - } - - if (!handled) { - handled = sendToTextFilter(keyCode, count, event); - } - - if (handled) { - return true; - } else { - switch (action) { - case KeyEvent.ACTION_DOWN: - return super.onKeyDown(keyCode, event); - case KeyEvent.ACTION_UP: - return super.onKeyUp(keyCode, event); - case KeyEvent.ACTION_MULTIPLE: - return super.onKeyMultiple(keyCode, count, event); - default: - return false; - } - } - } - - /** - * Scrolls up or down by the number of items currently present on screen. - * - * @param direction either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} - * @return whether selection was moved - */ - boolean pageScroll(int direction) { - int nextPage = -1; - - if (direction == FOCUS_UP) { - nextPage = Math.max(0, mSelectedPosition - getChildCount() - 1); - } else if (direction == FOCUS_DOWN) { - nextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount() - 1); - } - - if (nextPage >= 0) { - setSelectionInt(nextPage); - invokeOnItemScrollListener(); - return true; - } - - return false; - } - - /** - * Go to the last or first item if possible. - * - * @param direction either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}. - * - * @return Whether selection was moved. - */ - boolean fullScroll(int direction) { - boolean moved = false; - if (direction == FOCUS_UP) { - mLayoutMode = LAYOUT_SET_SELECTION; - setSelectionInt(0); - invokeOnItemScrollListener(); - moved = true; - } else if (direction == FOCUS_DOWN) { - mLayoutMode = LAYOUT_SET_SELECTION; - setSelectionInt(mItemCount - 1); - invokeOnItemScrollListener(); - moved = true; - } - - return moved; - } - - /** - * Scrolls to the next or previous item, horizontally or vertically. - * - * @param direction either {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, - * {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} - * - * @return whether selection was moved - */ - boolean arrowScroll(int direction) { - final int selectedPosition = mSelectedPosition; - final int numColumns = mNumColumns; - - int startOfRowPos; - int endOfRowPos; - - boolean moved = false; - - if (!mStackFromBottom) { - startOfRowPos = (selectedPosition / numColumns) * numColumns; - endOfRowPos = Math.min(startOfRowPos + numColumns - 1, mItemCount - 1); - } else { - final int invertedSelection = mItemCount - 1 - selectedPosition; - endOfRowPos = mItemCount - 1 - (invertedSelection / numColumns) * numColumns; - startOfRowPos = Math.max(0, endOfRowPos - numColumns + 1); - } - - switch (direction) { - case FOCUS_UP: - if (startOfRowPos > 0) { - mLayoutMode = LAYOUT_MOVE_SELECTION; - setSelectionInt(Math.max(0, selectedPosition - numColumns)); - moved = true; - } - break; - case FOCUS_DOWN: - if (endOfRowPos < mItemCount - 1) { - mLayoutMode = LAYOUT_MOVE_SELECTION; - setSelectionInt(Math.min(selectedPosition + numColumns, mItemCount - 1)); - moved = true; - } - break; - case FOCUS_LEFT: - if (selectedPosition > startOfRowPos) { - mLayoutMode = LAYOUT_MOVE_SELECTION; - setSelectionInt(selectedPosition - 1); - moved = true; - } - break; - case FOCUS_RIGHT: - if (selectedPosition < endOfRowPos) { - mLayoutMode = LAYOUT_MOVE_SELECTION; - setSelectionInt(selectedPosition + 1); - moved = true; - } - break; - } - - if (moved) { - playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); - invokeOnItemScrollListener(); - } - - return moved; - } - - @Override - protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - - int closestChildIndex = -1; - if (gainFocus && previouslyFocusedRect != null) { - previouslyFocusedRect.offset(mScrollX, mScrollY); - - // figure out which item should be selected based on previously - // focused rect - Rect otherRect = mTempRect; - int minDistance = Integer.MAX_VALUE; - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - // only consider view's on appropriate edge of grid - if (!isCandidateSelection(i, direction)) { - continue; - } - - final View other = getChildAt(i); - other.getDrawingRect(otherRect); - offsetDescendantRectToMyCoords(other, otherRect); - int distance = getDistance(previouslyFocusedRect, otherRect, direction); - - if (distance < minDistance) { - minDistance = distance; - closestChildIndex = i; - } - } - } - - if (closestChildIndex >= 0) { - setSelection(closestChildIndex + mFirstPosition); - } else { - requestLayout(); - } - } - - /** - * Is childIndex a candidate for next focus given the direction the focus - * change is coming from? - * @param childIndex The index to check. - * @param direction The direction, one of - * {FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT} - * @return Whether childIndex is a candidate. - */ - private boolean isCandidateSelection(int childIndex, int direction) { - final int count = getChildCount(); - final int invertedIndex = count - 1 - childIndex; - - int rowStart; - int rowEnd; - - if (!mStackFromBottom) { - rowStart = childIndex - (childIndex % mNumColumns); - rowEnd = Math.max(rowStart + mNumColumns - 1, count); - } else { - rowEnd = count - 1 - (invertedIndex - (invertedIndex % mNumColumns)); - rowStart = Math.max(0, rowEnd - mNumColumns + 1); - } - - switch (direction) { - case View.FOCUS_RIGHT: - // coming from left, selection is only valid if it is on left - // edge - return childIndex == rowStart; - case View.FOCUS_DOWN: - // coming from top; only valid if in top row - return rowStart == 0; - case View.FOCUS_LEFT: - // coming from right, must be on right edge - return childIndex == rowEnd; - case View.FOCUS_UP: - // coming from bottom, need to be in last row - return rowEnd == count - 1; - default: - throw new IllegalArgumentException("direction must be one of " - + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}."); - } - } - - /** - * Describes how the child views are horizontally aligned. Defaults to Gravity.LEFT - * - * @param gravity the gravity to apply to this grid's children - * - * @attr ref android.R.styleable#GridView_gravity - */ - public void setGravity(int gravity) { - if (mGravity != gravity) { - mGravity = gravity; - requestLayoutIfNecessary(); - } - } - - /** - * Set the amount of horizontal (x) spacing to place between each item - * in the grid. - * - * @param horizontalSpacing The amount of horizontal space between items, - * in pixels. - * - * @attr ref android.R.styleable#GridView_horizontalSpacing - */ - public void setHorizontalSpacing(int horizontalSpacing) { - if (horizontalSpacing != mRequestedHorizontalSpacing) { - mRequestedHorizontalSpacing = horizontalSpacing; - requestLayoutIfNecessary(); - } - } - - - /** - * Set the amount of vertical (y) spacing to place between each item - * in the grid. - * - * @param verticalSpacing The amount of vertical space between items, - * in pixels. - * - * @attr ref android.R.styleable#GridView_verticalSpacing - */ - public void setVerticalSpacing(int verticalSpacing) { - if (verticalSpacing != mVerticalSpacing) { - mVerticalSpacing = verticalSpacing; - requestLayoutIfNecessary(); - } - } - - /** - * Control how items are stretched to fill their space. - * - * @param stretchMode Either {@link #NO_STRETCH}, - * {@link #STRETCH_SPACING}, {@link #STRETCH_SPACING_UNIFORM}, or {@link #STRETCH_COLUMN_WIDTH}. - * - * @attr ref android.R.styleable#GridView_stretchMode - */ - public void setStretchMode(int stretchMode) { - if (stretchMode != mStretchMode) { - mStretchMode = stretchMode; - requestLayoutIfNecessary(); - } - } - - public int getStretchMode() { - return mStretchMode; - } - - /** - * Set the width of columns in the grid. - * - * @param columnWidth The column width, in pixels. - * - * @attr ref android.R.styleable#GridView_columnWidth - */ - public void setColumnWidth(int columnWidth) { - if (columnWidth != mRequestedColumnWidth) { - mRequestedColumnWidth = columnWidth; - requestLayoutIfNecessary(); - } - } - - /** - * Set the number of columns in the grid - * - * @param numColumns The desired number of columns. - * - * @attr ref android.R.styleable#GridView_numColumns - */ - public void setNumColumns(int numColumns) { - if (numColumns != mRequestedNumColumns) { - mRequestedNumColumns = numColumns; - requestLayoutIfNecessary(); - } - } - - /** - * Make sure views are touching the top or bottom edge, as appropriate for - * our gravity - */ - private void adjustViewsUpOrDown() { - final int childCount = getChildCount(); - - if (childCount > 0) { - int delta; - View child; - - if (!mStackFromBottom) { - // Uh-oh -- we came up short. Slide all views up to make them - // align with the top - child = getChildAt(0); - delta = child.getTop() - mListPadding.top; - if (mFirstPosition != 0) { - // It's OK to have some space above the first item if it is - // part of the vertical spacing - delta -= mVerticalSpacing; - } - if (delta < 0) { - // We only are looking to see if we are too low, not too high - delta = 0; - } - } else { - // we are too high, slide all views down to align with bottom - child = getChildAt(childCount - 1); - delta = child.getBottom() - (getHeight() - mListPadding.bottom); - - if (mFirstPosition + childCount < mItemCount) { - // It's OK to have some space below the last item if it is - // part of the vertical spacing - delta += mVerticalSpacing; - } - - if (delta > 0) { - // We only are looking to see if we are too high, not too low - delta = 0; - } - } - - if (delta != 0) { - offsetChildrenTopAndBottom(-delta); - } - } - } - - @Override - protected int computeVerticalScrollExtent() { - final int count = getChildCount(); - if (count > 0) { - final int numColumns = mNumColumns; - final int rowCount = (count + numColumns - 1) / numColumns; - - int extent = rowCount * 100; - - View view = getChildAt(0); - final int top = view.getTop(); - int height = view.getHeight(); - if (height > 0) { - extent += (top * 100) / height; - } - - view = getChildAt(count - 1); - final int bottom = view.getBottom(); - height = view.getHeight(); - if (height > 0) { - extent -= ((bottom - getHeight()) * 100) / height; - } - - return extent; - } - return 0; - } - - @Override - protected int computeVerticalScrollOffset() { - if (mFirstPosition >= 0 && getChildCount() > 0) { - final View view = getChildAt(0); - final int top = view.getTop(); - int height = view.getHeight(); - if (height > 0) { - final int whichRow = mFirstPosition / mNumColumns; - return Math.max(whichRow * 100 - (top * 100) / height, 0); - } - } - return 0; - } - - @Override - protected int computeVerticalScrollRange() { - // TODO: Account for vertical spacing too - final int numColumns = mNumColumns; - final int rowCount = (mItemCount + numColumns - 1) / numColumns; - return Math.max(rowCount * 100, 0); - } -} - |
