Android Open Source - Aviary-Android-SDK Aviary Gallery






From Project

Back to project page Aviary-Android-SDK.

License

The source code is released under:

AVIARY API TERMS OF USE Full Legal Agreement The following terms and conditions and the terms and conditions at http://www.aviary.com/terms (collectively, the ?Terms??) govern your use of any and ...

If you think the Android project Aviary-Android-SDK listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.aviary.android.feather.widget;
/*w w w . j  a  v a  2s .  co  m*/
import android.content.Context;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.v4.widget.EdgeEffectCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListView;

import com.aviary.android.feather.R;
import com.aviary.android.feather.widget.ScrollerRunnable.ScrollableView;

public class AviaryGallery extends AviaryAbsSpinner implements GestureDetector.OnGestureListener, ScrollableView, VibrationWidget {

  public interface OnItemsScrollListener {

    void onScrollStarted( AviaryAdapterView<?> parent, View view, int position, long id );

    void onScroll( AviaryAdapterView<?> parent, View view, int position, long id );

    void onScrollFinished( AviaryAdapterView<?> parent, View view, int position, long id );
  }

  @SuppressWarnings ( "unused" )
  private static final int SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT = 250;

  // helper for this View vibration
  private VibrationHelper mVibratorHelper;

  // auto select child while scrolling
  private boolean mAutoSelectChild = false;

  private OnItemsScrollListener mItemsScrollListener = null;

  // center of this View
  private int mCenter = 0;

  // animation duration
  private int mAnimationDuration;

  // left and right edges
  private EdgeEffectCompat mEdgeGlowLeft;
  private EdgeEffectCompat mEdgeGlowRight;

  // vertical gravity for children
  private int mGravity = Gravity.CENTER_VERTICAL;

  private GestureDetector mGestureDetector;

  // remember the touch down position
  private int mDownTouchPosition;

  private boolean isDown;

  // scroller helper
  private ScrollerRunnable mScroller;

  private boolean mAutoScrollToCenter = true;

  private int mTouchSlop;

  ScrollCompletedSelectionNotifier mScrollCompletedNotifier;
  ScrollScrollSelectionNotifier mScrollScrollNotifier;

  private int mRealSelectedPosition;

  // Whether to continuously callback on the item selected listener during a fling.
  private boolean mShouldCallbackDuringFling = false;

  // 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 = true;

  /**
   * 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;

  private boolean mIsFirstScroll;

  @SuppressWarnings ( "unused" )
  private int mLastMotionValue;

  /**
   * Instantiates a new gallery.
   * 
   * @param context
   *            the context
   */
  public AviaryGallery ( Context context ) {
    this( context, null );
  }

  /**
   * Instantiates a new gallery.
   * 
   * @param context
   *            the context
   * @param attrs
   *            the attrs
   */
  public AviaryGallery ( Context context, AttributeSet attrs ) {
    this( context, attrs, R.attr.aviaryGalleryStyle );
  }

  /**
   * Instantiates a new gallery.
   * 
   * @param context
   *            the context
   * @param attrs
   *            the attrs
   * @param defStyle
   *            the def style
   */
  public AviaryGallery ( Context context, AttributeSet attrs, int defStyle ) {
    super( context, attrs, defStyle );

    Theme theme = context.getTheme();
    TypedArray a = theme.obtainStyledAttributes( attrs, R.styleable.AviaryGallery, defStyle, 0 );

    mAnimationDuration = a.getInteger( R.styleable.AviaryGallery_android_animationDuration, 400 );

    a.recycle();

    mGestureDetector = new GestureDetector( context, this );
    mGestureDetector.setIsLongpressEnabled( false );

    ViewConfiguration configuration = ViewConfiguration.get( context );
    mTouchSlop = configuration.getScaledTouchSlop();

    mScroller = new ScrollerRunnable( this, mAnimationDuration, configuration.getScaledOverscrollDistance(), new DecelerateInterpolator( 1.0f ) );
    mVibratorHelper = new VibrationHelper( context, true );
  }

  /**
   * if true then the center item will be always selected by default while scrolling
   * otherwise the center item will be selected only when the movement has completed
   * 
   * @param value
   */
  public void setAutoSelectChild( boolean value ) {
    mAutoSelectChild = value;
  }

  public boolean getAutoSelectChild() {
    return mAutoSelectChild;
  }

  @Override
  public void setVibrationEnabled( boolean value ) {
    mVibratorHelper.setEnabled( value );
  }

  @Override
  public boolean getVibrationEnabled() {
    return mVibratorHelper.enabled();
  }

  /**
   * Sets the on items scroll listener.
   * 
   * @param value
   *            the new on items scroll listener
   */
  public void setOnItemsScrollListener( OnItemsScrollListener value ) {
    mItemsScrollListener = value;
  }

  @Override
  public void setOverScrollMode( int overScrollMode ) {
    super.setOverScrollMode( overScrollMode );

    if ( overScrollMode != OVER_SCROLL_NEVER ) {
      if ( mEdgeGlowLeft == null ) {
        mEdgeGlowLeft = new EdgeEffectCompat( getContext() );
        mEdgeGlowRight = new EdgeEffectCompat( getContext() );
      }
    } else {
      mEdgeGlowLeft = mEdgeGlowRight = null;
    }
  }

  @Override
  protected void dispatchDraw( Canvas canvas ) {
    super.dispatchDraw( canvas );

    if ( getChildCount() > 0 ) {
      drawEdges( canvas );
    }
  }

  private void drawEdges( Canvas canvas ) {

    if ( mEdgeGlowLeft != null ) {
      if ( !mEdgeGlowLeft.isFinished() ) {
        final int restoreCount = canvas.save();
        final int height = getHeight() - getPaddingTop() - getPaddingBottom();

        canvas.rotate( 270 );
        canvas.translate( -height + getPaddingTop(), 0 );
        mEdgeGlowLeft.setSize( height, getWidth() );
        if ( mEdgeGlowLeft.draw( canvas ) ) {
          postInvalidate();
        }
        canvas.restoreToCount( restoreCount );
      }
      if ( !mEdgeGlowRight.isFinished() ) {
        final int restoreCount = canvas.save();
        final int width = getWidth();
        final int height = getHeight() - getPaddingTop() - getPaddingBottom();

        canvas.rotate( 90 );
        canvas.translate( -getPaddingTop(), -width );
        mEdgeGlowRight.setSize( height, width );
        if ( mEdgeGlowRight.draw( canvas ) ) {
          postInvalidate();
        }
        canvas.restoreToCount( restoreCount );
      }
    }
  }

  /**
   * Sets the auto scroll to center.
   * 
   * @param value
   *            the new auto scroll to center
   */
  public void setAutoScrollToCenter( boolean value ) {
    mAutoScrollToCenter = value;
  }

  /**
   * 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;
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    removeCallbacks( mScrollCompletedNotifier );
    removeCallbacks( mScrollScrollNotifier );
  }

  /**
   * 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;
  }

  public void setAnimationDuration( int animationDurationMillis ) {
    mAnimationDuration = animationDurationMillis;
  }

  @Override
  protected int computeHorizontalScrollExtent() {
    return 1;
  }

  @Override
  protected int computeHorizontalScrollOffset() {
    return mSelectedPosition;
  }

  @Override
  protected int computeHorizontalScrollRange() {
    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() {
    /*
     * AviaryGallery expects AviaryGallery.LayoutParams.
     */
    return new AviaryGallery.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT );
  }

  @Override
  protected void onLayout( boolean changed, int l, int t, int r, int b ) {
    Log.i( VIEW_LOG_TAG, "onLayout: " + changed );
    super.onLayout( changed, l, t, r, b );

    mInLayout = true;
    layout( 0, false, changed );
    mInLayout = false;
  }

  @Override
  int getChildHeight( View child ) {
    return child.getMeasuredHeight();
  }

  @Override
  public void trackMotionScroll( int delta ) {
    if ( delta == 0 ) return;

    int deltaX = mScroller.getLastFlingX() - delta;

    // Pretend that each frame of a fling scroll is a touch scroll
    if ( delta > 0 ) {
      mDownTouchPosition = mFirstPosition;
      // Don't fling more than 1 screen
      // delta = Math.min( getWidth() - mPaddingLeft - mPaddingRight - 1, delta );
      delta = Math.min( getWidth() - 0 - 0 - 1, delta );
    } else {
      mDownTouchPosition = ( mFirstPosition + getChildCount() - 1 );
      // Don't fling more than 1 screen
      // delta = Math.max( -( getWidth() - mPaddingRight - mPaddingLeft - 1 ), delta
      // );
      delta = Math.max( -( getWidth() - 0 - 0 - 1 ), delta );
    }

    if ( getChildCount() == 0 ) {
      return;
    }

    boolean toLeft = deltaX < 0;

    int limitedDeltaX = deltaX;

    if ( !mScroller.isFinished() ) {
      int realDeltaX = getLimitedMotionScrollAmount( toLeft, deltaX );
      if ( realDeltaX != deltaX ) {
        if ( !toLeft ) { // <<<
          if ( null != mEdgeGlowLeft ) mEdgeGlowLeft.onAbsorb( (int) mScroller.getCurrVelocity() );
        } else { // >>>
          if ( null != mEdgeGlowRight ) mEdgeGlowRight.onAbsorb( (int) mScroller.getCurrVelocity() );
        }
        limitedDeltaX = realDeltaX;
      }
    }

    if ( limitedDeltaX != deltaX ) {
      mScroller.endFling( false );
      if ( limitedDeltaX == 0 ) 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();
    }

    setSelectionToCenterChild();
    onScrollChanged( 0, 0, 0, 0 ); // dummy values, View's implementation does not use
                    // these.
    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 ) + ( motionToLeft ? extremeChild.getWidth() / 2 : -extremeChild.getWidth() / 2 );
    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 );
  }

  /**
   * Gets the over scroll delta.
   * 
   * @param motionToLeft
   *            the motion to left
   * @param deltaX
   *            the delta x
   * @return the over scroll delta
   */
  int getOverScrollDelta( int deltaX ) {
    boolean motionToLeft = deltaX > 0;
    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 - deltaX < galleryCenter ) {
        return extremeChildCenter - galleryCenter;
      }
    } else {
      if ( extremeChildCenter - deltaX > galleryCenter ) {
        return extremeChildCenter - galleryCenter;
      }
    }
    return deltaX;
  }

  @Override
  protected void onOverScrolled( int scrollX, int scrollY, boolean clampedX, boolean clampedY ) {}

  /**
   * 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 );
    }
  }

  private int getCenterOfGallery() {
    return mCenter;
  }

  @Override
  protected void onSizeChanged( int w, int h, int oldw, int oldh ) {
    super.onSizeChanged( w, h, oldw, oldh );
    // mCenter = ( w - mPaddingLeft - mPaddingRight ) / 2 + mPaddingLeft;
    mCenter = w / 2;

  }

  /**
   * Gets the center of view.
   * 
   * @param view
   *            the view
   * @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.: AviaryGallery bounds).
   * 
   * @param toLeft
   *            Whether to detach children to the left of the AviaryGallery, 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 = 0; // mPaddingLeft;
      for ( int i = 0; i < numChildren; i++ ) {
        int n = i;
        final View child = getChildAt( n );
        if ( child.getRight() >= galleryLeft ) {
          break;
        } else {
          start = n;
          count++;

          int viewType = mAdapter.getItemViewType( firstPosition + n );
          mRecycleBin.get( viewType ).add( child );
        }
      }
      start = 0;

    } else {
      final int galleryRight = getWidth(); // - mPaddingRight;
      for ( int i = numChildren - 1; i >= 0; i-- ) {
        int n = i;
        final View child = getChildAt( n );
        if ( child.getLeft() <= galleryRight ) {
          break;
        } else {
          start = n;
          count++;

          int viewType = mAdapter.getItemViewType( firstPosition + n );
          mRecycleBin.get( viewType ).add( 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).
   */
  @Override
  public void scrollIntoSlots() {
    if ( getChildCount() == 0 ) return;

    if ( mAutoScrollToCenter ) {

      View view = getChildAt( mSelectedPosition - mFirstPosition );
      if ( null == view ) return;

      int selectedCenter = getCenterOfView( view );
      int targetCenter = getCenterOfGallery();
      int scrollAmount = targetCenter - selectedCenter;

      if ( scrollAmount != 0 ) {
        mScroller.startUsingDistance( 0, -scrollAmount );
      } else {
        onFinishedMovement();
      }
    } else {
      onFinishedMovement();
    }
  }

  /**
   * Scrolls the items so that the selected item is in its 'slot' (its center is the
   * gallery's center).
   * 
   * @return true, if is over scrolled
   */
  private boolean isOverScrolled() {
    if ( getChildCount() < 2 ) return false;

    if ( mSelectedPosition == 0 || mSelectedPosition == mItemCount - 1 ) {

      View view = getChildAt( mSelectedPosition - mFirstPosition );
      if ( null == view ) return false;

      int selectedCenter0 = getCenterOfView( view );
      int targetCenter = getCenterOfGallery();

      if ( mSelectedPosition == 0 && selectedCenter0 > targetCenter ) return true;
      if ( ( mSelectedPosition == mItemCount - 1 ) && selectedCenter0 < targetCenter ) return true;
    }
    return false;
  }

  private void onFinishedMovement() {
    Log.i( VIEW_LOG_TAG, "onFinishedMovement" );

    if ( isDown ) return;

    if ( mSuppressSelectionChanged ) {
      mSuppressSelectionChanged = false;
      selectionChanged( mSelectedPosition, mSelectedPosition );
    }

    scrollCompleted();

    if ( !mAutoSelectChild ) {

      View centerChild = getSelectedView();

      if ( mRealSelectedPosition != mSelectedPosition ) {

        View old = getChildAt( mRealSelectedPosition - mFirstPosition );
        if ( null != old ) {
          old.setSelected( false );
        }
      }

      mRealSelectedPosition = mSelectedPosition;

      if ( null != centerChild ) {
        centerChild.setSelected( true );
      }
    }

    invalidate();
  }

  /**
   * Looks for the child that is closest to the center and sets it as the selected
   * child.
   */
  private void setSelectionToCenterChild() {
    final View view = getChildAt( mSelectedPosition - mFirstPosition );

    if ( null == view ) return;

    int galleryCenter = getCenterOfGallery();

    // Common case where the current selected position is correct
    if ( view.getLeft() <= galleryCenter && view.getRight() >= galleryCenter ) {
      return;
    }

    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 ) {
        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 ) {

      newPos = Math.min( Math.max( newPos, 0 ), mItemCount - 1 );
      setSelectedPositionInt( newPos, true );
      setNextSelectedPositionInt( newPos );
      checkSelectionChanged();
    }
  }

  /**
   * Creates and positions all views for this AviaryGallery.
   * <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.
   * @param animate
   *            the animate
   */
  @Override
  void layout( int delta, boolean animate, boolean changed ) {

    if ( !changed && delta == 0 ) {
      layoutChildren();
      return;
    }

    int childrenLeft = 0; // mSpinnerPadding.left;
    int childrenWidth = getRight() - getLeft();// - mSpinnerPadding.left -
                          // mSpinnerPadding.right;

    Log.i( VIEW_LOG_TAG, "layout. delta: " + delta + ", animate: " + animate + ", changed: " + changed + ", inLayout: " + mInLayout );

    if ( mDataChanged ) {
      handleDataChanged();
    }

    // Handle an empty gallery by removing all views.
    if ( mItemCount == 0 ) {
      resetList();
      return;
    }

    // Update to the new selected position.
    Log.d( VIEW_LOG_TAG, "mNextSelectedPosition: " + mNextSelectedPosition );
    if ( mNextSelectedPosition >= 0 ) {
      mRealSelectedPosition = mNextSelectedPosition;
      setSelectedPositionInt( mNextSelectedPosition, animate );
    }

    // All views go in recycler while we are in layout
    mFirstPosition = mSelectedPosition;

    if ( getChildCount() == 0 || changed || delta != 0 ) {
      recycleAllViews();
      emptySubRecycler();
      removeAllViewsInLayout();

      // Make selected view and center it
      View sel = makeAndAddView( mSelectedPosition, 0, 0, true );

      // Put the selected child in the center
      int selectedOffset = childrenLeft + ( childrenWidth / 2 ) - ( sel.getWidth() / 2 );
      sel.offsetLeftAndRight( selectedOffset );

      fillToGalleryRight();
      fillToGalleryLeft();
    }

    // checkSelectionChanged();

    mDataChanged = false;
    mNeedSync = false;

    mSuppressSelectionChanged = false;
    setNextSelectedPositionInt( mSelectedPosition );
    checkSelectionChanged();
    mSuppressSelectionChanged = true;

    Log.d( VIEW_LOG_TAG, "layout complete" );
    postInvalidate();
  }

  private void fillToGalleryLeft() {
    int galleryLeft = 0; // 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 = getRight() - getLeft();// - mPaddingRight;
    }

    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 galleryRight = getRight() - getLeft();// - mPaddingRight;
    int numChildren = getChildCount();

    // 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 = 0; // mPaddingLeft;
    }

    while ( curLeftEdge < galleryRight /* && curPosition < numItems */) {
      prevIterationView = makeAndAddView( curPosition, curPosition - mSelectedPosition, curLeftEdge, true );

      // Set state for next iteration
      curLeftEdge = prevIterationView.getRight();// + itemSpacing;
      curPosition++;
    }
  }

  private View makeAndAddView( int position, int offset, int x, boolean fromLeft ) {

    View child = null;
    int viewType = mAdapter.getItemViewType( position );

    if ( !mDataChanged ) {
      child = mRecycleBin.get( viewType ).poll();

      if ( child != null ) {
        child = mAdapter.getView( position, child, this );
        setUpChild( child, offset, x, fromLeft );
      }
    }

    if ( null == child ) {
      child = mAdapter.getView( position, null, this );
      setUpChild( child, offset, x, fromLeft );
    }

    // auto select children is false
    if ( !mAutoSelectChild && null != child ) {
      child.setSelected( position == mRealSelectedPosition );
    }

    return child;
  }

  public void invalidateViews() {
    int count = getChildCount();
    for ( int i = 0; i < count; i++ ) {
      View child = getChildAt( i );
      mAdapter.getView( mFirstPosition + i, child, this );

      // auto select children is false
      if ( !mAutoSelectChild ) {
        child.setSelected( ( mFirstPosition + i ) == mRealSelectedPosition );
      }
    }
  }

  /**
   * Helper for makeAndAddView to set the position of a view and fill out its layout
   * parameters.
   * 
   * @param child
   *            The view to position
   * @param offset
   *            Offset from the selected position
   * @param x
   *            X-coordinate indicating where this view should be placed. This will
   *            either be the left or right edge of the view,
   *            depending on the fromLeft parameter
   * @param fromLeft
   *            Are we positioning views based on the left edge? (i.e., building from
   *            left to right)?
   */
  private void setUpChild( View child, int offset, int x, boolean fromLeft ) {

    AviaryGallery.LayoutParams lp = (AviaryGallery.LayoutParams) child.getLayoutParams();

    if ( lp == null ) {
      lp = (AviaryGallery.LayoutParams) generateDefaultLayoutParams();
    }

    addViewInLayout( child, fromLeft ? -1 : 0, lp );

    if ( mAutoSelectChild ) child.setSelected( offset == 0 );

    // Get measure specs
    int childHeightSpec = ViewGroup.getChildMeasureSpec( mHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height );
    int childWidthSpec = ViewGroup.getChildMeasureSpec( mWidthMeasureSpec, 0 /*
                                         * 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, 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 );
  }

  public void layoutChildren() {
    for ( int i = 0; i < getChildCount(); i++ ) {
      View child = getChildAt( i );
      forceChildLayout( child, child.getLayoutParams() );
      child.layout( child.getLeft(), child.getTop(), child.getRight(), child.getBottom() );
    }
  }

  public void forceChildLayout( View child, LayoutParams params ) {
    int childHeightSpec = ViewGroup.getChildMeasureSpec( mHeightMeasureSpec, getPaddingTop() + getPaddingBottom(), params.height );
    int childWidthSpec = ViewGroup.getChildMeasureSpec( mWidthMeasureSpec, getPaddingLeft() + getPaddingRight(), params.width );
    child.measure( childWidthSpec, childHeightSpec );
  }

  /**
   * Figure out vertical placement based on mGravity.
   * 
   * @param child
   *            Child to place
   * @param duringLayout
   *            the during layout
   * @return Where the top of the child should be
   */
  private int calculateTop( View child, boolean duringLayout ) {
    int myHeight = duringLayout ? getMeasuredHeight() : 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 ) {
      onUp();
    } else if ( action == MotionEvent.ACTION_CANCEL ) {
      onCancel();
    }

    return retValue;
  }

  @Override
  public boolean onSingleTapUp( MotionEvent e ) {

    if ( mDownTouchPosition >= 0 && mDownTouchPosition < mItemCount ) {

      if ( !scrollToChild( mDownTouchPosition - mFirstPosition ) ) {

        if ( mShouldCallbackOnUnselectedItemClick || mDownTouchPosition == mSelectedPosition ) {
          performItemClick( getChildAt( mDownTouchPosition - mFirstPosition ), mDownTouchPosition, mAdapter.getItemId( mDownTouchPosition ) );
        }
        return true;
      }
    }
    return false;
  }

  @Override
  public void onLongPress( MotionEvent e ) {}

  @Override
  public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) {

    if ( !mShouldCallbackDuringFling ) {
      mSuppressSelectionChanged = true;
    }

    int initialVelocity = (int) -velocityX / 2;
    int initialX = initialVelocity < 0 ? Integer.MAX_VALUE : 0;

    int delta = initialVelocity < 0 ? -2 : 2;
    int clampedDelta = getOverScrollDelta( delta );

    if ( delta == clampedDelta ) {
      mScroller.startUsingVelocity( initialX, initialVelocity );
      return true;
    } else {
      return false;
    }
  }

  @Override
  public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY ) {

    getParent().requestDisallowInterceptTouchEvent( true );

    mSuppressSelectionChanged = false;

    if ( mIsFirstScroll ) {

      if ( mItemsScrollListener != null ) {
        int selection = this.getSelectedItemPosition();
        if ( selection >= 0 ) {
          View v = getSelectedView();
          mItemsScrollListener.onScrollStarted( this, v, selection, getAdapter().getItemId( selection ) );
        }
      }

      if ( distanceX > 0 ) distanceX -= mTouchSlop;
      else distanceX += mTouchSlop;
    }

    int delta = (int) distanceX;
    int clampedDelta = getOverScrollDelta( delta );
    trackMotionScroll( clampedDelta );

    if ( null != mEdgeGlowLeft ) {
      // edges scroll
      if ( delta != clampedDelta ) {
        final boolean toLeft = delta > 0;
        float max_width;

        View view = getChildAt( 0 );
        if ( null != view ) {
          max_width = view.getWidth() * 2;
        } else {
          max_width = getWidth() / 2;
        }

        if ( !toLeft ) {
          mEdgeGlowLeft.onPull( -delta / max_width );
          if ( !mEdgeGlowRight.isFinished() ) {
            mEdgeGlowRight.onRelease();
          }
        } else {
          mEdgeGlowRight.onPull( delta / max_width );

          if ( !mEdgeGlowLeft.isFinished() ) {
            mEdgeGlowLeft.onRelease();
          }
        }
      }

      if ( !mEdgeGlowRight.isFinished() || !mEdgeGlowLeft.isFinished() ) {
        postInvalidate();
      }
    }

    mIsFirstScroll = false;
    return true;
  }

  @Override
  public boolean onDown( MotionEvent e ) {

    isDown = true;
    mScroller.stop( false );
    mDownTouchPosition = pointToPosition( (int) e.getX(), (int) e.getY() );

    if ( mDownTouchPosition >= 0 && mDownTouchPosition < mItemCount ) {
      View view = getChildAt( mDownTouchPosition - mFirstPosition );
      if ( null != view ) {
        // view.setPressed( true );
      }
    }
    mIsFirstScroll = true;
    return true;
  }

  void onUp() {
    isDown = false;
    if ( mScroller.isFinished() ) {
      scrollIntoSlots();
    } else {
      if ( isOverScrolled() ) {
        scrollIntoSlots();
      }
    }

    if ( mEdgeGlowLeft != null ) {
      mEdgeGlowLeft.onRelease();
      mEdgeGlowRight.onRelease();
    }
    dispatchUnpress();
  }

  void onCancel() {
    onUp();
  }

  @Override
  public void onShowPress( MotionEvent e ) {}

  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 ) {
    Log.i( VIEW_LOG_TAG, "dispatchSetSelected" );
  }

  @Override
  protected void dispatchSetPressed( boolean pressed ) {
    View view = getChildAt( mSelectedPosition - mFirstPosition );
    if ( view != null ) {
      // view.setPressed( pressed );
    }
  }

  @Override
  protected ContextMenuInfo getContextMenuInfo() {
    return mContextMenuInfo;
  }

  @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;
    }

    return false;
  }

  @Override
  public boolean dispatchKeyEvent( KeyEvent event ) {
    boolean handled = event.dispatch( this, null, null );
    return handled;
  }

  @Override
  public boolean onKeyUp( int keyCode, KeyEvent event ) {
    switch ( keyCode ) {
      case KeyEvent.KEYCODE_DPAD_CENTER:
      case KeyEvent.KEYCODE_ENTER: {

        if ( mReceivedInvokeKeyDown ) {
          if ( mItemCount > 0 ) {

            int selectedIndex = mSelectedPosition - mFirstPosition;
            View view = getChildAt( selectedIndex );
            if ( null != view ) {
              dispatchPress( view );
              postDelayed( new Runnable() {

                @Override
                public void run() {
                  dispatchUnpress();
                }
              }, ViewConfiguration.getPressedStateDuration() );

              performItemClick( view, mSelectedPosition, mAdapter.getItemId( mSelectedPosition ) );
            }
          }
        }
        mReceivedInvokeKeyDown = false;
        return true;
      }
    }
    return false;
  }

  public boolean movePrevious() {
    if ( mItemCount > 0 && mSelectedPosition > 0 ) {
      scrollToChild( mSelectedPosition - mFirstPosition - 1 );
      return true;
    } else {
      return false;
    }
  }

  public 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 ) {
      if ( mItemsScrollListener != null ) {
        int selection = this.getSelectedItemPosition();
        if ( selection >= 0 ) {
          View v = getSelectedView();
          mItemsScrollListener.onScrollStarted( this, v, selection, getAdapter().getItemId( selection ) );
        }
      }
      int distance = getCenterOfGallery() - getCenterOfView( child );
      mScroller.startUsingDistance( 0, -distance );
      return distance != 0;
    }
    return false;
  }

  void setSelectedPositionInt( int position, boolean animate ) {
    Log.i( VIEW_LOG_TAG, "setSelectedPositionInt: " + position + " from " + mSelectedPosition );
    mOldSelectedPosition = mSelectedPosition;
    super.setSelectedPositionInt( position );
    // updateSelectedItemMetadata( animate, false );
    // checkSelectionChanged();
  }

  private void fireVibration() {
    mVibratorHelper.vibrate( 10 );
  }

  /**
   * Describes how the child views are aligned.
   * 
   * @param gravity
   *            the new 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
   * after the selected child earlier one return
   * i + 1; } else { // Keep the children before the selected child the same return i; }
   * }
   */

  @Override
  protected void onFocusChanged( boolean gainFocus, int direction, Rect previouslyFocusedRect ) {
    super.onFocusChanged( gainFocus, direction, previouslyFocusedRect );

    View current = getChildAt( mSelectedPosition - mFirstPosition );

    if ( gainFocus && current != null ) {
      // current.requestFocus( direction );

      if ( mAutoSelectChild ) current.setSelected( true );
    }
  }

  private class ScrollCompletedSelectionNotifier implements Runnable {
    @Override
    public void run() {
      if ( mDataChanged ) {
        if ( getAdapter() != null ) {
          post( this );
        }
      } else {
        fireOnScrollCompleted();
      }
    }
  }

  private class ScrollScrollSelectionNotifier implements Runnable {

    @Override
    public void run() {
      if ( mDataChanged ) {
        if ( getAdapter() != null ) {
          post( this );
        }
      } else {
        fireOnSelected();
      }
    }
  }

  void scrollCompleted() {
    if ( mItemsScrollListener != null ) {
      if ( mInLayout || mBlockLayoutRequests ) {
        // If we are in a layout traversal, defer notification
        // by posting. This ensures that the view tree is
        // in a consistent state and is able to accomodate
        // new layout or invalidate requests.
        if ( mScrollCompletedNotifier == null ) {
          mScrollCompletedNotifier = new ScrollCompletedSelectionNotifier();
        }
        post( mScrollCompletedNotifier );
      } else {
        fireOnScrollCompleted();
      }
    }
  }

  private void fireOnScrollCompleted() {
    Log.i( VIEW_LOG_TAG, "fireOnScrollCompleted" );

    if ( mItemsScrollListener == null ) return;

    int selection = this.getSelectedItemPosition();
    if ( selection >= 0 && selection < mItemCount ) {
      View v = getSelectedView();
      mItemsScrollListener.onScrollFinished( this, v, selection, getAdapter().getItemId( selection ) );
    }
  }

  /**
   * We completely override the super method because we dont
   * want to use the AdapterView scroll listener
   */
  @Override
  void selectionChanged( int oldIndex, int newIndex ) {
    Log.i( VIEW_LOG_TAG, "selectionChanged: " + mSelectedPosition + "(" + oldIndex + "/" + newIndex + "), inLayout: " + mInLayout + ", suppress selection: "
        + mSuppressSelectionChanged );

    if ( mAutoSelectChild && oldIndex != newIndex ) {
      View oldSelectedChild = getChildAt( oldIndex - mFirstPosition );
      View newSelectedChild = getChildAt( newIndex - mFirstPosition );

      if ( null != newSelectedChild ) {
        newSelectedChild.setSelected( true );
      }

      if ( null != oldSelectedChild && oldSelectedChild != newSelectedChild ) {
        oldSelectedChild.setSelected( false );
      }
    }

    if ( !mSuppressSelectionChanged ) {

      if ( mItemsScrollListener != null ) {
        if ( mInLayout || mBlockLayoutRequests ) {
          // if ( mScrollScrollNotifier == null ) {
          // mScrollScrollNotifier = new ScrollScrollSelectionNotifier();
          // }
          // post( mScrollScrollNotifier );
        } else {
          fireOnSelected();
        }
      }

      // we fire selection events here not in View
      if ( mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode() ) {
        sendAccessibilityEvent( AccessibilityEvent.TYPE_VIEW_SELECTED );
      }
    }

    if ( oldIndex != newIndex && !mInLayout ) {
      // selection changed, fire the vibration
      fireVibration();
    }
  }

  @Override
  protected void fireOnSelected() {
    Log.i( VIEW_LOG_TAG, "fireOnSelected: current: " + mSelectedPosition + ", next: " + getSelectedItemPosition() + ", inLayout: " + mInLayout );

    if ( mItemsScrollListener == null ) return;

    View newSelectedChild = getSelectedView();
    int position = getSelectedItemPosition();
    mItemsScrollListener.onScroll( this, newSelectedChild, position, getAdapter().getItemId( position ) );
  }

  @Override
  public int getMinX() {
    return 0;
  }

  @Override
  public int getMaxX() {
    return Integer.MAX_VALUE;
  }

  /**
   * Return the current item at the center of the Gallery ( not the one currently
   * selected )
   * 
   * @return
   */
  @Override
  public int getSelectedItemPosition() {
    return super.getSelectedItemPosition();
  }

  /**
   * Return the current selected item position.
   * If the {@link #getAutoSelectChild()} is false, then the position is different from
   * the {@link #getSelectedItemPosition()}.
   * 
   * @return
   */
  public int getCurrentSelectedItemPosition() {
    if ( !mAutoSelectChild ) return mRealSelectedPosition;
    return mSelectedPosition;
  }

}




Java Source Code List

com.aviary.android.feather.AlertActivity.java
com.aviary.android.feather.AviaryMainController.java
com.aviary.android.feather.FeatherActivity.java
com.aviary.android.feather.async_tasks.AsyncImageManager.java
com.aviary.android.feather.async_tasks.DownloadImageAsyncTask.java
com.aviary.android.feather.async_tasks.ExifTask.java
com.aviary.android.feather.effects.AbstractContentPanel.java
com.aviary.android.feather.effects.AbstractOptionPanel.java
com.aviary.android.feather.effects.AbstractPanelLoaderService.java
com.aviary.android.feather.effects.AbstractPanel.java
com.aviary.android.feather.effects.AdjustEffectPanel.java
com.aviary.android.feather.effects.BordersPanel.java
com.aviary.android.feather.effects.ColorSplashPanel.java
com.aviary.android.feather.effects.CropPanel.java
com.aviary.android.feather.effects.DelayedSpotDrawPanel.java
com.aviary.android.feather.effects.DrawingPanel.java
com.aviary.android.feather.effects.EffectsPanel.java
com.aviary.android.feather.effects.EnhanceEffectPanel.java
com.aviary.android.feather.effects.MemePanel.java
com.aviary.android.feather.effects.NativeEffectRangePanel.java
com.aviary.android.feather.effects.SimpleStatusMachine.java
com.aviary.android.feather.effects.SliderEffectPanel.java
com.aviary.android.feather.effects.StickersPanel.java
com.aviary.android.feather.effects.TextPanel.java
com.aviary.android.feather.effects.TiltShiftPanel.java
com.aviary.android.feather.graphics.CdsPreviewTransformer.java
com.aviary.android.feather.graphics.GalleryBottomIndicatorDrawable.java
com.aviary.android.feather.graphics.GalleryTopIndicatorDrawable.java
com.aviary.android.feather.graphics.GlowBitmapDrawable.java
com.aviary.android.feather.graphics.GlowDrawable.java
com.aviary.android.feather.graphics.PluginDividerDrawable.java
com.aviary.android.feather.graphics.PreviewFillColorDrawable.java
com.aviary.android.feather.graphics.PreviewSpotDrawable.java
com.aviary.android.feather.graphics.RepeatableHorizontalDrawable.java
com.aviary.android.feather.opengl.AviaryGLSurfaceView.java
com.aviary.android.feather.utils.PackIconCallable.java
com.aviary.android.feather.utils.SimpleBitmapCache.java
com.aviary.android.feather.utils.ThreadUtils.java
com.aviary.android.feather.utils.TypefaceUtils.java
com.aviary.android.feather.utils.UIUtils.java
com.aviary.android.feather.widget.AdjustImageView.java
com.aviary.android.feather.widget.AviaryAbsSpinner.java
com.aviary.android.feather.widget.AviaryAdapterView.java
com.aviary.android.feather.widget.AviaryBadgeToolLayout.java
com.aviary.android.feather.widget.AviaryBottomBarViewFlipper.java
com.aviary.android.feather.widget.AviaryButton.java
com.aviary.android.feather.widget.AviaryEdgeEffect.java
com.aviary.android.feather.widget.AviaryGalleryTopIndicatorView.java
com.aviary.android.feather.widget.AviaryGallery.java
com.aviary.android.feather.widget.AviaryHighlightImageButton.java
com.aviary.android.feather.widget.AviaryImageRestoreSwitcher.java
com.aviary.android.feather.widget.AviaryImageSwitcher.java
com.aviary.android.feather.widget.AviaryNavBarViewFlipper.java
com.aviary.android.feather.widget.AviarySeekBar.java
com.aviary.android.feather.widget.AviaryTextView.java
com.aviary.android.feather.widget.AviaryToast.java
com.aviary.android.feather.widget.AviaryToggleButton.java
com.aviary.android.feather.widget.AviaryWheel.java
com.aviary.android.feather.widget.AviaryWorkspaceIndicator.java
com.aviary.android.feather.widget.AviaryWorkspace.java
com.aviary.android.feather.widget.CellLayout.java
com.aviary.android.feather.widget.CropImageView.java
com.aviary.android.feather.widget.DrawableHighlightView.java
com.aviary.android.feather.widget.EffectThumbLayout.java
com.aviary.android.feather.widget.HighlightView.java
com.aviary.android.feather.widget.IAPBuyButton.java
com.aviary.android.feather.widget.IAPDialogDetail.java
com.aviary.android.feather.widget.IAPDialogList.java
com.aviary.android.feather.widget.IAPDialogMain.java
com.aviary.android.feather.widget.ImageViewDrawableOverlay.java
com.aviary.android.feather.widget.ImageViewSpotDraw.java
com.aviary.android.feather.widget.ImageViewTiltiShiftTouch.java
com.aviary.android.feather.widget.ImageViewTouchAndDraw.java
com.aviary.android.feather.widget.PointCloud.java
com.aviary.android.feather.widget.ScrollerRunnable.java
com.aviary.android.feather.widget.VibrationHelper.java
com.aviary.android.feather.widget.VibrationWidget.java