This looks really useful, I'm going to have a play with it, many thanks for sharing.
Are you able to edit it so that it works with older android versions? I haven't come across the LayoutParams.MATCH_PARENT before. On Jun 14, 1:32 pm, droidful <austral...@gmail.com> wrote: > package com.droidful.flinggallery; > > import android.content.Context; > import android.util.Log; > import android.view.GestureDetector; > import android.view.KeyEvent; > import android.view.MotionEvent; > import android.view.View; > import android.view.animation.Animation; > import android.view.animation.AnimationUtils; > import android.view.animation.Interpolator; > import android.view.animation.Transformation; > import android.widget.Adapter; > import android.widget.FrameLayout; > import android.widget.LinearLayout; > > public class FlingGallery extends FrameLayout > { > // Constants > > private final int swipe_min_distance = 120; > private final int swipe_max_off_path = 250; > private final int swipe_threshold_veloicty = 400; > > // Properties > > private int mViewPaddingWidth = 0; > private int mAnimationDuration = 200; > private float mSnapBorderRatio = 0.4f; > private boolean mIsGalleryCircular = true; > > // Members > > private int mGalleryWidth = 0; > private boolean mIsTouched = false; > private boolean mIsDragging = false; > private float mCurrentOffset = 0.0f; > private long mScrollTimestamp = 0; > private int mFlingDirection = 0; > private int mCurrentPosition = 0; > private int mCurrentViewNumber = 0; > > private Context mContext; > private Adapter mAdapter; > private FlingGalleryView[] mViews; > private FlingGalleryAnimation mAnimation; > private GestureDetector mGestureDetector; > private Interpolator mDecelerateInterpolater; > > public FlingGallery(Context context) > { > super(context); > > mContext = context; > mAdapter = null; > > mViews = new FlingGalleryView[3]; > mViews[0] = new FlingGalleryView(0, this); > mViews[1] = new FlingGalleryView(1, this); > mViews[2] = new FlingGalleryView(2, this); > > mAnimation = new FlingGalleryAnimation(); > mGestureDetector = new GestureDetector(new > FlingGestureDetector()); > mDecelerateInterpolater = > AnimationUtils.loadInterpolator(mContext, > android.R.anim.decelerate_interpolator); > AnimationUtils.loadInterpolator(mContext, > android.R.anim.linear_interpolator); > } > > public void setPaddingWidth(int viewPaddingWidth) > { > mViewPaddingWidth = viewPaddingWidth; > } > > public void setAnimationDuration(int animationDuration) > { > mAnimationDuration = animationDuration; > } > > public void setSnapBorderRatio(float snapBorderRatio) > { > mSnapBorderRatio = snapBorderRatio; > } > > public void setIsGalleryCircular(boolean isGalleryCircular) > { > if (mIsGalleryCircular != isGalleryCircular) > { > mIsGalleryCircular = isGalleryCircular; > > if (mCurrentPosition == getFirstPosition()) > { > // We need to reload the view immediately to > the left to change it > to circular view or blank > > mViews[getPrevViewNumber(mCurrentViewNumber)].recycleView(getPrevPosition(mCurrentPosition)); > } > > if (mCurrentPosition == getLastPosition()) > { > // We need to reload the view immediately to > the right to change > it to circular view or blank > > mViews[getNextViewNumber(mCurrentViewNumber)].recycleView(getNextPosition(mCurrentPosition)); > } > } > } > > public int getGalleryCount() > { > return (mAdapter == null) ? 0 : mAdapter.getCount(); > } > > public int getFirstPosition() > { > return 0; > } > > public int getLastPosition() > { > return (getGalleryCount() == 0) ? 0 : getGalleryCount() - 1; > } > > private int getPrevPosition(int relativePosition) > { > int prevPosition = relativePosition - 1; > > if (prevPosition < getFirstPosition()) > { > prevPosition = getFirstPosition() - 1; > > if (mIsGalleryCircular == true) > { > prevPosition = getLastPosition(); > } > } > > return prevPosition; > } > > private int getNextPosition(int relativePosition) > { > int nextPosition = relativePosition + 1; > > if (nextPosition > getLastPosition()) > { > nextPosition = getLastPosition() + 1; > > if (mIsGalleryCircular == true) > { > nextPosition = getFirstPosition(); > } > } > > return nextPosition; > } > > private int getPrevViewNumber(int relativeViewNumber) > { > return (relativeViewNumber == 0) ? 2 : relativeViewNumber - 1; > } > > private int getNextViewNumber(int relativeViewNumber) > { > return (relativeViewNumber == 2) ? 0 : relativeViewNumber + 1; > } > > @Override > protected void onLayout(boolean changed, int left, int top, int > right, int bottom) > { > super.onLayout(changed, left, top, right, bottom); > > // Calculate our view width > mGalleryWidth = right - left; > > if (changed == true) > { > // Position views at correct starting offsets > mViews[0].setOffset(0, 0, mCurrentViewNumber); > mViews[1].setOffset(0, 0, mCurrentViewNumber); > mViews[2].setOffset(0, 0, mCurrentViewNumber); > } > } > > public void setAdapter(Adapter adapter) > { > mAdapter = adapter; > mCurrentPosition = 0; > mCurrentViewNumber = 0; > > // Load the initial views from adapter > mViews[0].recycleView(mCurrentPosition); > mViews[1].recycleView(getNextPosition(mCurrentPosition)); > mViews[2].recycleView(getPrevPosition(mCurrentPosition)); > > // Position views at correct starting offsets > mViews[0].setOffset(0, 0, mCurrentViewNumber); > mViews[1].setOffset(0, 0, mCurrentViewNumber); > mViews[2].setOffset(0, 0, mCurrentViewNumber); > } > > private int getViewOffset(int viewNumber, int relativeViewNumber) > { > // Determine width including configured padding width > int offsetWidth = mGalleryWidth + mViewPaddingWidth; > > // Position the previous view one measured width to left > if (viewNumber == getPrevViewNumber(relativeViewNumber)) > { > return offsetWidth; > } > > // Position the next view one measured width to the right > if (viewNumber == getNextViewNumber(relativeViewNumber)) > { > return offsetWidth * -1; > } > > return 0; > } > > void movePrevious() > { > // Slide to previous view > mFlingDirection = 1; > processGesture(); > } > > void moveNext() > { > // Slide to next view > mFlingDirection = -1; > processGesture(); > } > > �...@override > public boolean onKeyDown(int keyCode, KeyEvent event) > { > switch (keyCode) > { > case KeyEvent.KEYCODE_DPAD_LEFT: > movePrevious(); > return true; > > case KeyEvent.KEYCODE_DPAD_RIGHT: > moveNext(); > return true; > > case KeyEvent.KEYCODE_DPAD_CENTER: > case KeyEvent.KEYCODE_ENTER: > } > > return super.onKeyDown(keyCode, event); > } > > public boolean onGalleryTouchEvent(MotionEvent event) > { > boolean consumed = mGestureDetector.onTouchEvent(event); > > if (event.getAction() == MotionEvent.ACTION_UP) > { > if (mIsTouched || mIsDragging) > { > processScrollSnap(); > processGesture(); > } > } > > return consumed; > } > > void processGesture() > { > int newViewNumber = mCurrentViewNumber; > int reloadViewNumber = 0; > int reloadPosition = 0; > > mIsTouched = false; > mIsDragging = false; > > if (mFlingDirection > 0) > { > if (mCurrentPosition > getFirstPosition() || > mIsGalleryCircular == > true) > { > // Determine previous view and outgoing view > to recycle > newViewNumber = > getPrevViewNumber(mCurrentViewNumber); > mCurrentPosition = > getPrevPosition(mCurrentPosition); > reloadViewNumber = > getNextViewNumber(mCurrentViewNumber); > reloadPosition = > getPrevPosition(mCurrentPosition); > } > } > > if (mFlingDirection < 0) > { > if (mCurrentPosition < getLastPosition() || > mIsGalleryCircular == > true) > { > // Determine the next view and outgoing view > to recycle > newViewNumber = > getNextViewNumber(mCurrentViewNumber); > mCurrentPosition = > getNextPosition(mCurrentPosition); > reloadViewNumber = > getPrevViewNumber(mCurrentViewNumber); > reloadPosition = > getNextPosition(mCurrentPosition); > } > } > > if (newViewNumber != mCurrentViewNumber) > { > mCurrentViewNumber = newViewNumber; > > // Reload outgoing view from adapter in new position > mViews[reloadViewNumber].recycleView(reloadPosition); > } > > // Ensure input focus on the current view > mViews[mCurrentViewNumber].requestFocus(); > > // Run the slide animations for view transitions > mAnimation.prepareAnimation(mCurrentViewNumber); > this.startAnimation(mAnimation); > > // Reset fling state > mFlingDirection = 0; > } > > void processScrollSnap() > { > // Snap to next view if scrolled passed snap position > float rollEdgeWidth = mGalleryWidth * mSnapBorderRatio; > int rollOffset = mGalleryWidth - (int) rollEdgeWidth; > int currentOffset = > mViews[mCurrentViewNumber].getCurrentOffset(); > > if (currentOffset <= rollOffset * -1) > { > // Snap to previous view > mFlingDirection = 1; > } > > if (currentOffset >= rollOffset) > { > // Snap to next view > mFlingDirection = -1; > } > } > > private class FlingGalleryView > { > private int mViewNumber; > private FrameLayout mParentLayout; > > private FrameLayout mInvalidLayout = null; > private LinearLayout mInternalLayout = null; > private View mExternalView = null; > > public FlingGalleryView(int viewNumber, FrameLayout > parentLayout) > { > mViewNumber = viewNumber; > mParentLayout = parentLayout; > > // Invalid layout is used when outside gallery > mInvalidLayout = new FrameLayout(mContext); > mInvalidLayout.setLayoutParams(new > LinearLayout.LayoutParams( > LayoutParams.MATCH_PARENT, > LayoutParams.MATCH_PARENT)); > > // Internal layout is permanent for duration > mInternalLayout = new LinearLayout(mContext); > mInternalLayout.setLayoutParams(new > LinearLayout.LayoutParams( > LayoutParams.MATCH_PARENT, > LayoutParams.MATCH_PARENT)); > > mParentLayout.addView(mInternalLayout); > } > > public void recycleView(int newPosition) > { > if (mExternalView != null) > { > mInternalLayout.removeView(mExternalView); > } > > if (mAdapter != null) > { > mExternalView =... > > read more » -- You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en