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/Gallery.java | |
| parent | 076357b8567458d4b6dfdcf839ef751634cd2bfb (diff) | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/widget/Gallery.java')
| -rw-r--r-- | core/java/android/widget/Gallery.java | 1408 |
1 files changed, 0 insertions, 1408 deletions
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java deleted file mode 100644 index e7b303ada4d0..000000000000 --- a/core/java/android/widget/Gallery.java +++ /dev/null @@ -1,1408 +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 com.android.internal.R; - -import android.annotation.Widget; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.Config; -import android.util.Log; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.HapticFeedbackConstants; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.SoundEffectConstants; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.animation.Transformation; -import android.widget.AbsSpinner; -import android.widget.Scroller; - -/** - * A view that shows items in a center-locked, horizontally scrolling list. - * <p> - * The default values for the Gallery assume you will be using - * {@link android.R.styleable#Theme_galleryItemBackground} as the background for - * each View given to the Gallery from the Adapter. If you are not doing this, - * you may need to adjust some Gallery properties, such as the spacing. - * <p> - * Views given to the Gallery should use {@link Gallery.LayoutParams} as their - * layout parameters type. - * - * @attr ref android.R.styleable#Gallery_animationDuration - * @attr ref android.R.styleable#Gallery_spacing - * @attr ref android.R.styleable#Gallery_gravity - */ -@Widget -public class Gallery extends AbsSpinner implements GestureDetector.OnGestureListener { - - private static final String TAG = "Gallery"; - - private static final boolean localLOGV = Config.LOGV; - - /** - * Duration in milliseconds from the start of a scroll during which we're - * unsure whether the user is scrolling or flinging. - */ - private static final int SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT = 250; - - /** - * Horizontal spacing between items. - */ - private int mSpacing = 0; - - /** - * How long the transition animation should run when a child view changes - * position, measured in milliseconds. - */ - private int mAnimationDuration = 400; - - /** - * The alpha of items that are not selected. - */ - private float mUnselectedAlpha; - - /** - * Left most edge of a child seen so far during layout. - */ - private int mLeftMost; - - /** - * Right most edge of a child seen so far during layout. - */ - private int mRightMost; - - private int mGravity; - - /** - * Helper for detecting touch gestures. - */ - private GestureDetector mGestureDetector; - - /** - * The position of the item that received the user's down touch. - */ - private int mDownTouchPosition; - - /** - * The view of the item that received the user's down touch. - */ - private View mDownTouchView; - - /** - * Executes the delta scrolls from a fling or scroll movement. - */ - private FlingRunnable mFlingRunnable = new FlingRunnable(); - - /** - * Sets mSuppressSelectionChanged = false. This is used to set it to false - * in the future. It will also trigger a selection changed. - */ - private Runnable mDisableSuppressSelectionChangedRunnable = new Runnable() { - public void run() { - mSuppressSelectionChanged = false; - selectionChanged(); - } - }; - - /** - * When fling runnable runs, it resets this to false. Any method along the - * path until the end of its run() can set this to true to abort any - * remaining fling. For example, if we've reached either the leftmost or - * rightmost item, we will set this to true. - */ - private boolean mShouldStopFling; - - /** - * The currently selected item's child. - */ - private View mSelectedChild; - - /** - * Whether to continuously callback on the item selected listener during a - * fling. - */ - private boolean mShouldCallbackDuringFling = true; - - /** - * Whether to callback when an item that is not selected is clicked. - */ - private boolean mShouldCallbackOnUnselectedItemClick = true; - - /** - * If true, do not callback to item selected listener. - */ - private boolean mSuppressSelectionChanged; - - /** - * If true, we have received the "invoke" (center or enter buttons) key - * down. This is checked before we action on the "invoke" key up, and is - * subsequently cleared. - */ - private boolean mReceivedInvokeKeyDown; - - private AdapterContextMenuInfo mContextMenuInfo; - - /** - * If true, this onScroll is the first for this user's drag (remember, a - * drag sends many onScrolls). - */ - private boolean mIsFirstScroll; - - public Gallery(Context context) { - this(context, null); - } - - public Gallery(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.galleryStyle); - } - - public Gallery(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - mGestureDetector = new GestureDetector(context, this); - mGestureDetector.setIsLongpressEnabled(true); - - TypedArray a = context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.Gallery, defStyle, 0); - - int index = a.getInt(com.android.internal.R.styleable.Gallery_gravity, -1); - if (index >= 0) { - setGravity(index); - } - - int animationDuration = - a.getInt(com.android.internal.R.styleable.Gallery_animationDuration, -1); - if (animationDuration > 0) { - setAnimationDuration(animationDuration); - } - - int spacing = - a.getDimensionPixelOffset(com.android.internal.R.styleable.Gallery_spacing, 0); - setSpacing(spacing); - - float unselectedAlpha = a.getFloat( - com.android.internal.R.styleable.Gallery_unselectedAlpha, 0.5f); - setUnselectedAlpha(unselectedAlpha); - - a.recycle(); - - // We draw the selected item last (because otherwise the item to the - // right overlaps it) - mGroupFlags |= FLAG_USE_CHILD_DRAWING_ORDER; - - mGroupFlags |= FLAG_SUPPORT_STATIC_TRANSFORMATIONS; - } - - /** - * Whether or not to callback on any {@link #getOnItemSelectedListener()} - * while the items are being flinged. If false, only the final selected item - * will cause the callback. If true, all items between the first and the - * final will cause callbacks. - * - * @param shouldCallback Whether or not to callback on the listener while - * the items are being flinged. - */ - public void setCallbackDuringFling(boolean shouldCallback) { - mShouldCallbackDuringFling = shouldCallback; - } - - /** - * Whether or not to callback when an item that is not selected is clicked. - * If false, the item will become selected (and re-centered). If true, the - * {@link #getOnItemClickListener()} will get the callback. - * - * @param shouldCallback Whether or not to callback on the listener when a - * item that is not selected is clicked. - * @hide - */ - public void setCallbackOnUnselectedItemClick(boolean shouldCallback) { - mShouldCallbackOnUnselectedItemClick = shouldCallback; - } - - /** - * Sets how long the transition animation should run when a child view - * changes position. Only relevant if animation is turned on. - * - * @param animationDurationMillis The duration of the transition, in - * milliseconds. - * - * @attr ref android.R.styleable#Gallery_animationDuration - */ - public void setAnimationDuration(int animationDurationMillis) { - mAnimationDuration = animationDurationMillis; - } - - /** - * Sets the spacing between items in a Gallery - * - * @param spacing The spacing in pixels between items in the Gallery - * - * @attr ref android.R.styleable#Gallery_spacing - */ - public void setSpacing(int spacing) { - mSpacing = spacing; - } - - /** - * Sets the alpha of items that are not selected in the Gallery. - * - * @param unselectedAlpha the alpha for the items that are not selected. - * - * @attr ref android.R.styleable#Gallery_unselectedAlpha - */ - public void setUnselectedAlpha(float unselectedAlpha) { - mUnselectedAlpha = unselectedAlpha; - } - - @Override - protected boolean getChildStaticTransformation(View child, Transformation t) { - - t.clear(); - t.setAlpha(child == mSelectedChild ? 1.0f : mUnselectedAlpha); - - return true; - } - - @Override - protected int computeHorizontalScrollExtent() { - // Only 1 item is considered to be selected - return 1; - } - - @Override - protected int computeHorizontalScrollOffset() { - // Current scroll position is the same as the selected position - return mSelectedPosition; - } - - @Override - protected int computeHorizontalScrollRange() { - // Scroll range is the same as the item count - return mItemCount; - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return new LayoutParams(p); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - /* - * Gallery expects Gallery.LayoutParams. - */ - return new Gallery.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - - /* - * Remember that we are in layout to prevent more layout request from - * being generated. - */ - mInLayout = true; - layout(0, false); - mInLayout = false; - } - - @Override - int getChildHeight(View child) { - return child.getMeasuredHeight(); - } - - /** - * Tracks a motion scroll. In reality, this is used to do just about any - * movement to items (touch scroll, arrow-key scroll, set an item as selected). - * - * @param deltaX Change in X from the previous event. - */ - void trackMotionScroll(int deltaX) { - - if (getChildCount() == 0) { - return; - } - - boolean toLeft = deltaX < 0; - - int limitedDeltaX = getLimitedMotionScrollAmount(toLeft, deltaX); - if (limitedDeltaX != deltaX) { - // The above call returned a limited amount, so stop any scrolls/flings - mFlingRunnable.endFling(false); - onFinishedMovement(); - } - - offsetChildrenLeftAndRight(limitedDeltaX); - - detachOffScreenChildren(toLeft); - - if (toLeft) { - // If moved left, there will be empty space on the right - fillToGalleryRight(); - } else { - // Similarly, empty space on the left - fillToGalleryLeft(); - } - - // Clear unused views - mRecycler.clear(); - - setSelectionToCenterChild(); - - invalidate(); - } - - int getLimitedMotionScrollAmount(boolean motionToLeft, int deltaX) { - int extremeItemPosition = motionToLeft ? mItemCount - 1 : 0; - View extremeChild = getChildAt(extremeItemPosition - mFirstPosition); - - if (extremeChild == null) { - return deltaX; - } - - int extremeChildCenter = getCenterOfView(extremeChild); - int galleryCenter = getCenterOfGallery(); - - if (motionToLeft) { - if (extremeChildCenter <= galleryCenter) { - - // The extreme child is past his boundary point! - return 0; - } - } else { - if (extremeChildCenter >= galleryCenter) { - - // The extreme child is past his boundary point! - return 0; - } - } - - int centerDifference = galleryCenter - extremeChildCenter; - - return motionToLeft - ? Math.max(centerDifference, deltaX) - : Math.min(centerDifference, deltaX); - } - - /** - * Offset the horizontal location of all children of this view by the - * specified number of pixels. - * - * @param offset the number of pixels to offset - */ - private void offsetChildrenLeftAndRight(int offset) { - for (int i = getChildCount() - 1; i >= 0; i--) { - getChildAt(i).offsetLeftAndRight(offset); - } - } - - /** - * @return The center of this Gallery. - */ - private int getCenterOfGallery() { - return (getWidth() - mPaddingLeft - mPaddingRight) / 2 + mPaddingLeft; - } - - /** - * @return The center of the given view. - */ - private static int getCenterOfView(View view) { - return view.getLeft() + view.getWidth() / 2; - } - - /** - * Detaches children that are off the screen (i.e.: Gallery bounds). - * - * @param toLeft Whether to detach children to the left of the Gallery, or - * to the right. - */ - private void detachOffScreenChildren(boolean toLeft) { - int numChildren = getChildCount(); - int firstPosition = mFirstPosition; - int start = 0; - int count = 0; - - if (toLeft) { - final int galleryLeft = mPaddingLeft; - for (int i = 0; i < numChildren; i++) { - final View child = getChildAt(i); - if (child.getRight() >= galleryLeft) { - break; - } else { - count++; - mRecycler.put(firstPosition + i, child); - } - } - } else { - final int galleryRight = getWidth() - mPaddingRight; - for (int i = numChildren - 1; i >= 0; i--) { - final View child = getChildAt(i); - if (child.getLeft() <= galleryRight) { - break; - } else { - start = i; - count++; - mRecycler.put(firstPosition + i, child); - } - } - } - - detachViewsFromParent(start, count); - - if (toLeft) { - mFirstPosition += count; - } - } - - /** - * Scrolls the items so that the selected item is in its 'slot' (its center - * is the gallery's center). - */ - private void scrollIntoSlots() { - - if (getChildCount() == 0 || mSelectedChild == null) return; - - int selectedCenter = getCenterOfView(mSelectedChild); - int targetCenter = getCenterOfGallery(); - - int scrollAmount = targetCenter - selectedCenter; - if (scrollAmount != 0) { - mFlingRunnable.startUsingDistance(scrollAmount); - } else { - onFinishedMovement(); - } - } - - private void onFinishedMovement() { - if (mSuppressSelectionChanged) { - mSuppressSelectionChanged = false; - - // We haven't been callbacking during the fling, so do it now - super.selectionChanged(); - } - } - - @Override - void selectionChanged() { - if (!mSuppressSelectionChanged) { - super.selectionChanged(); - } - } - - /** - * Looks for the child that is closest to the center and sets it as the - * selected child. - */ - private void setSelectionToCenterChild() { - - View selView = mSelectedChild; - if (mSelectedChild == null) return; - - int galleryCenter = getCenterOfGallery(); - - if (selView != null) { - - // Common case where the current selected position is correct - if (selView.getLeft() <= galleryCenter && selView.getRight() >= galleryCenter) { - return; - } - } - - // TODO better search - int closestEdgeDistance = Integer.MAX_VALUE; - int newSelectedChildIndex = 0; - for (int i = getChildCount() - 1; i >= 0; i--) { - - View child = getChildAt(i); - - if (child.getLeft() <= galleryCenter && child.getRight() >= galleryCenter) { - // This child is in the center - newSelectedChildIndex = i; - break; - } - - int childClosestEdgeDistance = Math.min(Math.abs(child.getLeft() - galleryCenter), - Math.abs(child.getRight() - galleryCenter)); - if (childClosestEdgeDistance < closestEdgeDistance) { - closestEdgeDistance = childClosestEdgeDistance; - newSelectedChildIndex = i; - } - } - - int newPos = mFirstPosition + newSelectedChildIndex; - - if (newPos != mSelectedPosition) { - setSelectedPositionInt(newPos); - setNextSelectedPositionInt(newPos); - checkSelectionChanged(); - } - } - - /** - * Creates and positions all views for this Gallery. - * <p> - * We layout rarely, most of the time {@link #trackMotionScroll(int)} takes - * care of repositioning, adding, and removing children. - * - * @param delta Change in the selected position. +1 means the selection is - * moving to the right, so views are scrolling to the left. -1 - * means the selection is moving to the left. - */ - @Override - void layout(int delta, boolean animate) { - - int childrenLeft = mSpinnerPadding.left; - int childrenWidth = mRight - mLeft - mSpinnerPadding.left - mSpinnerPadding.right; - - if (mDataChanged) { - handleDataChanged(); - } - - // Handle an empty gallery by removing all views. - if (mItemCount == 0) { - resetList(); - return; - } - - // Update to the new selected position. - if (mNextSelectedPosition >= 0) { - setSelectedPositionInt(mNextSelectedPosition); - } - - // All views go in recycler while we are in layout - recycleAllViews(); - - // Clear out old views - //removeAllViewsInLayout(); - detachAllViewsFromParent(); - - /* - * These will be used to give initial positions to views entering the - * gallery as we scroll - */ - mRightMost = 0; - mLeftMost = 0; - - // Make selected view and center it - - /* - * mFirstPosition will be decreased as we add views to the left later - * on. The 0 for x will be offset in a couple lines down. - */ - mFirstPosition = mSelectedPosition; - View sel = makeAndAddView(mSelectedPosition, 0, 0, true); - - // Put the selected child in the center - Gallery.LayoutParams lp = (Gallery.LayoutParams) sel.getLayoutParams(); - int selectedOffset = childrenLeft + (childrenWidth / 2) - (sel.getWidth() / 2); - sel.offsetLeftAndRight(selectedOffset); - - fillToGalleryRight(); - fillToGalleryLeft(); - - // Flush any cached views that did not get reused above - mRecycler.clear(); - - invalidate(); - checkSelectionChanged(); - - mDataChanged = false; - mNeedSync = false; - setNextSelectedPositionInt(mSelectedPosition); - - updateSelectedItemMetadata(); - } - - private void fillToGalleryLeft() { - int itemSpacing = mSpacing; - int galleryLeft = mPaddingLeft; - - // Set state for initial iteration - View prevIterationView = getChildAt(0); - int curPosition; - int curRightEdge; - - if (prevIterationView != null) { - curPosition = mFirstPosition - 1; - curRightEdge = prevIterationView.getLeft() - itemSpacing; - } else { - // No children available! - curPosition = 0; - curRightEdge = mRight - mLeft - mPaddingRight; - mShouldStopFling = true; - } - - while (curRightEdge > galleryLeft && curPosition >= 0) { - prevIterationView = makeAndAddView(curPosition, curPosition - mSelectedPosition, - curRightEdge, false); - - // Remember some state - mFirstPosition = curPosition; - - // Set state for next iteration - curRightEdge = prevIterationView.getLeft() - itemSpacing; - curPosition--; - } - } - - private void fillToGalleryRight() { - int itemSpacing = mSpacing; - int galleryRight = mRight - mLeft - mPaddingRight; - int numChildren = getChildCount(); - int numItems = mItemCount; - - // Set state for initial iteration - View prevIterationView = getChildAt(numChildren - 1); - int curPosition; - int curLeftEdge; - - if (prevIterationView != null) { - curPosition = mFirstPosition + numChildren; - curLeftEdge = prevIterationView.getRight() + itemSpacing; - } else { - mFirstPosition = curPosition = mItemCount - 1; - curLeftEdge = mPaddingLeft; - mShouldStopFling = true; - } - - while (curLeftEdge < galleryRight && curPosition < numItems) { - prevIterationView = makeAndAddView(curPosition, curPosition - mSelectedPosition, - curLeftEdge, true); - - // Set state for next iteration - curLeftEdge = prevIterationView.getRight() + itemSpacing; - curPosition++; - } - } - - /** - * Obtain a view, either by pulling an existing view from the recycler or by - * getting a new one from the adapter. If we are animating, make sure there - * is enough information in the view's layout parameters to animate from the - * old to new positions. - * - * @param position Position in the gallery for the view to obtain - * @param offset Offset from the selected position - * @param x X-coordintate indicating where this view should be placed. This - * will either be the left or right edge of the view, depending on - * the fromLeft paramter - * @param fromLeft Are we posiitoning views based on the left edge? (i.e., - * building from left to right)? - * @return A view that has been added to the gallery - */ - private View makeAndAddView(int position, int offset, int x, - boolean fromLeft) { - - View child; - - if (!mDataChanged) { - child = mRecycler.get(position); - if (child != null) { - // Can reuse an existing view - Gallery.LayoutParams lp = (Gallery.LayoutParams) - child.getLayoutParams(); - - int childLeft = child.getLeft(); - - // Remember left and right edges of where views have been placed - mRightMost = Math.max(mRightMost, childLeft - + child.getMeasuredWidth()); - mLeftMost = Math.min(mLeftMost, childLeft); - - // Position the view - setUpChild(child, offset, x, fromLeft); - - return child; - } - } - - // Nothing found in the recycler -- ask the adapter for a view - child = mAdapter.getView(position, null, this); - - // Position the view - setUpChild(child, offset, x, fromLeft); - - return child; - } - - /** - * Helper for makeAndAddView to set the position of a view and fill out its - * layout paramters. - * - * @param child The view to position - * @param offset Offset from the selected position - * @param x X-coordintate indicating where this view should be placed. This - * will either be the left or right edge of the view, depending on - * the fromLeft paramter - * @param fromLeft Are we posiitoning views based on the left edge? (i.e., - * building from left to right)? - */ - private void setUpChild(View child, int offset, int x, boolean fromLeft) { - - // Respect layout params that are already in the view. Otherwise - // make some up... - Gallery.LayoutParams lp = (Gallery.LayoutParams) - child.getLayoutParams(); - if (lp == null) { - lp = (Gallery.LayoutParams) generateDefaultLayoutParams(); - } - - addViewInLayout(child, fromLeft ? -1 : 0, lp); - - child.setSelected(offset == 0); - - // Get measure specs - int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec, - mSpinnerPadding.top + mSpinnerPadding.bottom, lp.height); - int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, - mSpinnerPadding.left + mSpinnerPadding.right, lp.width); - - // Measure child - child.measure(childWidthSpec, childHeightSpec); - - int childLeft; - int childRight; - - // Position vertically based on gravity setting - int childTop = calculateTop(child, lp, true); - int childBottom = childTop + child.getMeasuredHeight(); - - int width = child.getMeasuredWidth(); - if (fromLeft) { - childLeft = x; - childRight = childLeft + width; - } else { - childLeft = x - width; - childRight = x; - } - - child.layout(childLeft, childTop, childRight, childBottom); - } - - /** - * Figure out vertical placement based on mGravity - * - * @param child Child to place - * @param lp LayoutParams for this view (just so we don't keep looking them - * up) - * @return Where the top of the child should be - */ - private int calculateTop(View child, Gallery.LayoutParams lp, boolean duringLayout) { - int myHeight = duringLayout ? mMeasuredHeight : getHeight(); - int childHeight = duringLayout ? child.getMeasuredHeight() : child.getHeight(); - - int childTop = 0; - - switch (mGravity) { - case Gravity.TOP: - childTop = mSpinnerPadding.top; - break; - case Gravity.CENTER_VERTICAL: - int availableSpace = myHeight - mSpinnerPadding.bottom - - mSpinnerPadding.top - childHeight; - childTop = mSpinnerPadding.top + (availableSpace / 2); - break; - case Gravity.BOTTOM: - childTop = myHeight - mSpinnerPadding.bottom - childHeight; - break; - } - return childTop; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - - // Give everything to the gesture detector - boolean retValue = mGestureDetector.onTouchEvent(event); - - int action = event.getAction(); - if (action == MotionEvent.ACTION_UP) { - // Helper method for lifted finger - onUp(); - } else if (action == MotionEvent.ACTION_CANCEL) { - onCancel(); - } - - return retValue; - } - - /** - * {@inheritDoc} - */ - public boolean onSingleTapUp(MotionEvent e) { - - if (mDownTouchPosition >= 0) { - - // An item tap should make it selected, so scroll to this child. - scrollToChild(mDownTouchPosition - mFirstPosition); - - // Also pass the click so the client knows, if it wants to. - if (mShouldCallbackOnUnselectedItemClick || mDownTouchPosition == mSelectedPosition) { - performItemClick(mDownTouchView, mDownTouchPosition, mAdapter - .getItemId(mDownTouchPosition)); - } - - return true; - } - - return false; - } - - /** - * {@inheritDoc} - */ - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - - if (!mShouldCallbackDuringFling) { - // We want to suppress selection changes - - // Remove any future code to set mSuppressSelectionChanged = false - removeCallbacks(mDisableSuppressSelectionChangedRunnable); - - // This will get reset once we scroll into slots - if (!mSuppressSelectionChanged) mSuppressSelectionChanged = true; - } - - // Fling the gallery! - mFlingRunnable.startUsingVelocity((int) -velocityX); - - return true; - } - - /** - * {@inheritDoc} - */ - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - - if (localLOGV) Log.v(TAG, String.valueOf(e2.getX() - e1.getX())); - - /* - * Now's a good time to tell our parent to stop intercepting our events! - * The user has moved more than the slop amount, since GestureDetector - * ensures this before calling this method. Also, if a parent is more - * interested in this touch's events than we are, it would have - * intercepted them by now (for example, we can assume when a Gallery is - * in the ListView, a vertical scroll would not end up in this method - * since a ListView would have intercepted it by now). - */ - mParent.requestDisallowInterceptTouchEvent(true); - - // As the user scrolls, we want to callback selection changes so related- - // info on the screen is up-to-date with the gallery's selection - if (!mShouldCallbackDuringFling) { - if (mIsFirstScroll) { - /* - * We're not notifying the client of selection changes during - * the fling, and this scroll could possibly be a fling. Don't - * do selection changes until we're sure it is not a fling. - */ - if (!mSuppressSelectionChanged) mSuppressSelectionChanged = true; - postDelayed(mDisableSuppressSelectionChangedRunnable, SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT); - } - } else { - if (mSuppressSelectionChanged) mSuppressSelectionChanged = false; - } - - // Track the motion - trackMotionScroll(-1 * (int) distanceX); - - mIsFirstScroll = false; - return true; - } - - /** - * {@inheritDoc} - */ - public boolean onDown(MotionEvent e) { - - // Kill any existing fling/scroll - mFlingRunnable.stop(false); - - // Get the item's view that was touched - mDownTouchPosition = pointToPosition((int) e.getX(), (int) e.getY()); - - if (mDownTouchPosition >= 0) { - mDownTouchView = getChildAt(mDownTouchPosition - mFirstPosition); - mDownTouchView.setPressed(true); - } - - // Reset the multiple-scroll tracking state - mIsFirstScroll = true; - - // Must return true to get matching events for this down event. - return true; - } - - /** - * Called when a touch event's action is MotionEvent.ACTION_UP. - */ - void onUp() { - - if (mFlingRunnable.mScroller.isFinished()) { - scrollIntoSlots(); - } - - dispatchUnpress(); - } - - /** - * Called when a touch event's action is MotionEvent.ACTION_CANCEL. - */ - void onCancel() { - onUp(); - } - - /** - * {@inheritDoc} - */ - public void onLongPress(MotionEvent e) { - - if (mDownTouchPosition < 0) { - return; - } - - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - long id = getItemIdAtPosition(mDownTouchPosition); - dispatchLongPress(mDownTouchView, mDownTouchPosition, id); - } - - // Unused methods from GestureDetector.OnGestureListener below - - /** - * {@inheritDoc} - */ - public void onShowPress(MotionEvent e) { - } - - // Unused methods from GestureDetector.OnGestureListener above - - private void dispatchPress(View child) { - - if (child != null) { - child.setPressed(true); - } - - setPressed(true); - } - - private void dispatchUnpress() { - - for (int i = getChildCount() - 1; i >= 0; i--) { - getChildAt(i).setPressed(false); - } - - setPressed(false); - } - - @Override - public void dispatchSetSelected(boolean selected) { - /* - * We don't want to pass the selected state given from its parent to its - * children since this widget itself has a selected state to give to its - * children. - */ - } - - @Override - protected void dispatchSetPressed(boolean pressed) { - - // Show the pressed state on the selected child - if (mSelectedChild != null) { - mSelectedChild.setPressed(pressed); - } - } - - @Override - protected ContextMenuInfo getContextMenuInfo() { - return mContextMenuInfo; - } - - @Override - public boolean showContextMenuForChild(View originalView) { - - final int longPressPosition = getPositionForView(originalView); - if (longPressPosition < 0) { - return false; - } - - final long longPressId = mAdapter.getItemId(longPressPosition); - return dispatchLongPress(originalView, longPressPosition, longPressId); - } - - @Override - public boolean showContextMenu() { - - if (isPressed() && mSelectedPosition >= 0) { - int index = mSelectedPosition - mFirstPosition; - View v = getChildAt(index); - return dispatchLongPress(v, mSelectedPosition, mSelectedRowId); - } - - return false; - } - - private boolean dispatchLongPress(View view, int position, long id) { - boolean handled = false; - - if (mOnItemLongClickListener != null) { - handled = mOnItemLongClickListener.onItemLongClick(this, mDownTouchView, - mDownTouchPosition, id); - } - - if (!handled) { - mContextMenuInfo = new AdapterContextMenuInfo(view, position, id); - handled = super.showContextMenuForChild(this); - } - - if (handled) { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } - - return handled; - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // Gallery steals all key events - return event.dispatch(this); - } - - /** - * Handles left, right, and clicking - * @see android.view.View#onKeyDown - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - - case KeyEvent.KEYCODE_DPAD_LEFT: - if (movePrevious()) { - playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT); - } - return true; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (moveNext()) { - playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT); - } - return true; - - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: - mReceivedInvokeKeyDown = true; - // fallthrough to default handling - } - - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: { - - if (mReceivedInvokeKeyDown) { - if (mItemCount > 0) { - - dispatchPress(mSelectedChild); - postDelayed(new Runnable() { - public void run() { - dispatchUnpress(); - } - }, ViewConfiguration.getPressedStateDuration()); - - int selectedIndex = mSelectedPosition - mFirstPosition; - performItemClick(getChildAt(selectedIndex), mSelectedPosition, mAdapter - .getItemId(mSelectedPosition)); - } - } - - // Clear the flag - mReceivedInvokeKeyDown = false; - - return true; - } - } - - return super.onKeyUp(keyCode, event); - } - - boolean movePrevious() { - if (mItemCount > 0 && mSelectedPosition > 0) { - scrollToChild(mSelectedPosition - mFirstPosition - 1); - return true; - } else { - return false; - } - } - - boolean moveNext() { - if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) { - scrollToChild(mSelectedPosition - mFirstPosition + 1); - return true; - } else { - return false; - } - } - - private boolean scrollToChild(int childPosition) { - View child = getChildAt(childPosition); - - if (child != null) { - int distance = getCenterOfGallery() - getCenterOfView(child); - mFlingRunnable.startUsingDistance(distance); - return true; - } - - return false; - } - - @Override - void setSelectedPositionInt(int position) { - super.setSelectedPositionInt(position); - - // Updates any metadata we keep about the selected item. - updateSelectedItemMetadata(); - } - - private void updateSelectedItemMetadata() { - - View oldSelectedChild = mSelectedChild; - - View child = mSelectedChild = getChildAt(mSelectedPosition - mFirstPosition); - if (child == null) { - return; - } - - child.setSelected(true); - child.setFocusable(true); - - if (hasFocus()) { - child.requestFocus(); - } - - // We unfocus the old child down here so the above hasFocus check - // returns true - if (oldSelectedChild != null) { - - // Make sure its drawable state doesn't contain 'selected' - oldSelectedChild.setSelected(false); - - // Make sure it is not focusable anymore, since otherwise arrow keys - // can make this one be focused - oldSelectedChild.setFocusable(false); - } - - } - - /** - * Describes how the child views are aligned. - * @param gravity - * - * @attr ref android.R.styleable#Gallery_gravity - */ - public void setGravity(int gravity) - { - if (mGravity != gravity) { - mGravity = gravity; - requestLayout(); - } - } - - @Override - protected int getChildDrawingOrder(int childCount, int i) { - int selectedIndex = mSelectedPosition - mFirstPosition; - - // Just to be safe - if (selectedIndex < 0) return i; - - if (i == childCount - 1) { - // Draw the selected child last - return selectedIndex; - } else if (i >= selectedIndex) { - // Move the children to the right of the selected child earlier one - return i + 1; - } else { - // Keep the children to the left of the selected child the same - return i; - } - } - - @Override - protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - - /* - * The gallery shows focus by focusing the selected item. So, give - * focus to our selected item instead. We steal keys from our - * selected item elsewhere. - */ - if (gainFocus && mSelectedChild != null) { - mSelectedChild.requestFocus(direction); - } - - } - - /** - * Responsible for fling behavior. Use {@link #startUsingVelocity(int)} to - * initiate a fling. Each frame of the fling is handled in {@link #run()}. - * A FlingRunnable will keep re-posting itself until the fling is done. - * - */ - private class FlingRunnable implements Runnable { - /** - * Tracks the decay of a fling scroll - */ - private Scroller mScroller; - - /** - * X value reported by mScroller on the previous fling - */ - private int mLastFlingX; - - public FlingRunnable() { - mScroller = new Scroller(getContext()); - } - - private void startCommon() { - // Remove any pending flings - removeCallbacks(this); - } - - public void startUsingVelocity(int initialVelocity) { - if (initialVelocity == 0) return; - - startCommon(); - - int initialX = initialVelocity < 0 ? Integer.MAX_VALUE : 0; - mLastFlingX = initialX; - mScroller.fling(initialX, 0, initialVelocity, 0, - 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE); - post(this); - } - - public void startUsingDistance(int distance) { - if (distance == 0) return; - - startCommon(); - - mLastFlingX = 0; - mScroller.startScroll(0, 0, -distance, 0, mAnimationDuration); - post(this); - } - - public void stop(boolean scrollIntoSlots) { - removeCallbacks(this); - endFling(scrollIntoSlots); - } - - private void endFling(boolean scrollIntoSlots) { - /* - * Force the scroller's status to finished (without setting its - * position to the end) - */ - mScroller.forceFinished(true); - - if (scrollIntoSlots) scrollIntoSlots(); - } - - public void run() { - - if (mItemCount == 0) { - endFling(true); - return; - } - - mShouldStopFling = false; - - final Scroller scroller = mScroller; - boolean more = scroller.computeScrollOffset(); - final int x = scroller.getCurrX(); - - // Flip sign to convert finger direction to list items direction - // (e.g. finger moving down means list is moving towards the top) - int delta = mLastFlingX - x; - - // Pretend that each frame of a fling scroll is a touch scroll - if (delta > 0) { - // Moving towards the left. Use first view as mDownTouchPosition - mDownTouchPosition = mFirstPosition; - - // Don't fling more than 1 screen - delta = Math.min(getWidth() - mPaddingLeft - mPaddingRight - 1, delta); - } else { - // Moving towards the right. Use last view as mDownTouchPosition - int offsetToLast = getChildCount() - 1; - mDownTouchPosition = mFirstPosition + offsetToLast; - - // Don't fling more than 1 screen - delta = Math.max(-(getWidth() - mPaddingRight - mPaddingLeft - 1), delta); - } - - trackMotionScroll(delta); - - if (more && !mShouldStopFling) { - mLastFlingX = x; - post(this); - } else { - endFling(true); - } - } - - } - - /** - * Gallery extends LayoutParams to provide a place to hold current - * Transformation information along with previous position/transformation - * info. - * - */ - public static class LayoutParams extends ViewGroup.LayoutParams { - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - } - - public LayoutParams(int w, int h) { - super(w, h); - } - - public LayoutParams(ViewGroup.LayoutParams source) { - super(source); - } - } -} |
