summaryrefslogtreecommitdiff
path: root/core/java/android/widget/GridView.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:45 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:45 -0800
commitd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /core/java/android/widget/GridView.java
parent076357b8567458d4b6dfdcf839ef751634cd2bfb (diff)
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/widget/GridView.java')
-rw-r--r--core/java/android/widget/GridView.java1842
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);
- }
-}
-