Android拖动效果图
转载至:http://iaiai.iteye.com/blog/1580587
先来看效果图:
就两个文件,新建一个项目把下面两个文件放进去,设置好启动activity就行了
FlingGallery.java
- package com.iaiai.fg;
- import android.content.Context;
- 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;
- // TODO:
- // 1. In order to improve performance Cache screen bitmap and use for animation
- // 2. Establish superfluous memory allocations and delay or replace with reused objects
- // Probably need to make sure we are not allocating objects (strings, etc.) in loops
- /**
- *
- * <p>
- * Title: FlingGallery.java
- * </p>
- * <p>
- * E-Mail: 176291935@qq.com
- * </p>
- * <p>
- * QQ: 176291935
- * </p>
- * <p>
- * Http: iaiai.iteye.com
- * </p>
- * <p>
- * Create time: 2012-7-7 上午10:18:53
- * </p>
- *
- * @author 丸子
- * @version 0.0.1
- */
- 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 = 250;
- private float mSnapBorderRatio = 0.5f;
- 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);
- }
- 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) {
- if (newPosition >= getFirstPosition()
- && newPosition <= getLastPosition()) {
- mExternalView = mAdapter.getView(newPosition,
- mExternalView, mInternalLayout);
- } else {
- mExternalView = mInvalidLayout;
- }
- }
- if (mExternalView != null) {
- mInternalLayout.addView(mExternalView,
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- }
- }
- public void setOffset(int xOffset, int yOffset, int relativeViewNumber) {
- // Scroll the target view relative to its own position relative to
- // currently displayed view
- mInternalLayout.scrollTo(
- getViewOffset(mViewNumber, relativeViewNumber) + xOffset,
- yOffset);
- }
- public int getCurrentOffset() {
- // Return the current scroll position
- return mInternalLayout.getScrollX();
- }
- public void requestFocus() {
- mInternalLayout.requestFocus();
- }
- }
- private class FlingGalleryAnimation extends Animation {
- private boolean mIsAnimationInProgres;
- private int mRelativeViewNumber;
- private int mInitialOffset;
- private int mTargetOffset;
- private int mTargetDistance;
- public FlingGalleryAnimation() {
- mIsAnimationInProgres = false;
- mRelativeViewNumber = 0;
- mInitialOffset = 0;
- mTargetOffset = 0;
- mTargetDistance = 0;
- }
- public void prepareAnimation(int relativeViewNumber) {
- // If we are animating relative to a new view
- if (mRelativeViewNumber != relativeViewNumber) {
- if (mIsAnimationInProgres == true) {
- // We only have three views so if requested again to animate
- // in same direction we must snap
- int newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1
- : -1;
- int animDirection = (mTargetDistance < 0) ? 1 : -1;
- // If animation in same direction
- if (animDirection == newDirection) {
- // Ran out of time to animate so snap to the target
- // offset
- mViews[0].setOffset(mTargetOffset, 0,
- mRelativeViewNumber);
- mViews[1].setOffset(mTargetOffset, 0,
- mRelativeViewNumber);
- mViews[2].setOffset(mTargetOffset, 0,
- mRelativeViewNumber);
- }
- }
- // Set relative view number for animation
- mRelativeViewNumber = relativeViewNumber;
- }
- // Note: In this implementation the targetOffset will always be zero
- // as we are centering the view; but we include the calculations of
- // targetOffset and targetDistance for use in future implementations
- mInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset();
- mTargetOffset = getViewOffset(mRelativeViewNumber,
- mRelativeViewNumber);
- mTargetDistance = mTargetOffset - mInitialOffset;
- // Configure base animation properties
- this.setDuration(mAnimationDuration);
- this.setInterpolator(mDecelerateInterpolater);
- // Start/continued animation
- mIsAnimationInProgres = true;
- }
- @Override
- protected void applyTransformation(float interpolatedTime,
- Transformation transformation) {
- // Ensure interpolatedTime does not over-shoot then calculate new
- // offset
- interpolatedTime = (interpolatedTime > 1.0f) ? 1.0f
- : interpolatedTime;
- int offset = mInitialOffset
- + (int) (mTargetDistance * interpolatedTime);
- for (int viewNumber = 0; viewNumber < 3; viewNumber++) {
- // Only need to animate the visible views as the other view will
- // always be off-screen
- if ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber))
- || (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber))) {
- mViews[viewNumber]
- .setOffset(offset, 0, mRelativeViewNumber);
- }
- }
- }
- @Override
- public boolean getTransformation(long currentTime,
- Transformation outTransformation) {
- if (super.getTransformation(currentTime, outTransformation) == false) {
- // Perform final adjustment to offsets to cleanup animation
- mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);
- mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);
- mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber);
- // Reached the animation target
- mIsAnimationInProgres = false;
- return false;
- }
- // Cancel if the screen touched
- if (mIsTouched || mIsDragging) {
- // Note that at this point we still consider ourselves to be
- // animating
- // because we have not yet reached the target offset; its just
- // that the
- // user has temporarily interrupted the animation with a touch
- // gesture
- return false;
- }
- return true;
- }
- }
- private class FlingGestureDetector extends
- GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onDown(MotionEvent e) {
- // Stop animation
- mIsTouched = true;
- // Reset fling state
- mFlingDirection = 0;
- return true;
- }
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2,
- float distanceX, float distanceY) {
- if (e2.getAction() == MotionEvent.ACTION_MOVE) {
- if (mIsDragging == false) {
- // Stop animation
- mIsTouched = true;
- // Reconfigure scroll
- mIsDragging = true;
- mFlingDirection = 0;
- mScrollTimestamp = System.currentTimeMillis();
- mCurrentOffset = mViews[mCurrentViewNumber]
- .getCurrentOffset();
- }
- float maxVelocity = mGalleryWidth
- / (mAnimationDuration / 1000.0f);
- long timestampDelta = System.currentTimeMillis()
- - mScrollTimestamp;
- float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f);
- float currentScrollDelta = e1.getX() - e2.getX();
- if (currentScrollDelta < maxScrollDelta * -1)
- currentScrollDelta = maxScrollDelta * -1;
- if (currentScrollDelta > maxScrollDelta)
- currentScrollDelta = maxScrollDelta;
- int scrollOffset = Math.round(mCurrentOffset
- + currentScrollDelta);
- // We can't scroll more than the width of our own frame layout
- if (scrollOffset >= mGalleryWidth)
- scrollOffset = mGalleryWidth;
- if (scrollOffset <= mGalleryWidth * -1)
- scrollOffset = mGalleryWidth * -1;
- mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber);
- mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber);
- mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber);
- }
- return false;
- }
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- if (Math.abs(e1.getY() - e2.getY()) <= swipe_max_off_path) {
- if (e2.getX() - e1.getX() > swipe_min_distance
- && Math.abs(velocityX) > swipe_threshold_veloicty) {
- movePrevious();
- }
- if (e1.getX() - e2.getX() > swipe_min_distance
- && Math.abs(velocityX) > swipe_threshold_veloicty) {
- moveNext();
- }
- }
- return false;
- }
- @Override
- public void onLongPress(MotionEvent e) {
- // Finalise scrolling
- mFlingDirection = 0;
- processGesture();
- }
- @Override
- public void onShowPress(MotionEvent e) {
- }
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- // Reset fling state
- mFlingDirection = 0;
- return false;
- }
- }
- }
package com.iaiai.fg;
import android.content.Context;
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;
// TODO:
// 1. In order to improve performance Cache screen bitmap and use for animation
// 2. Establish superfluous memory allocations and delay or replace with reused objects
// Probably need to make sure we are not allocating objects (strings, etc.) in loops
/**
*
* <p>
* Title: FlingGallery.java
* </p>
* <p>
* E-Mail: 176291935@qq.com
* </p>
* <p>
* QQ: 176291935
* </p>
* <p>
* Http: iaiai.iteye.com
* </p>
* <p>
* Create time: 2012-7-7 上午10:18:53
* </p>
*
* @author 丸子
* @version 0.0.1
*/
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 = 250;
private float mSnapBorderRatio = 0.5f;
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);
}
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) {
if (newPosition >= getFirstPosition()
&& newPosition <= getLastPosition()) {
mExternalView = mAdapter.getView(newPosition,
mExternalView, mInternalLayout);
} else {
mExternalView = mInvalidLayout;
}
}
if (mExternalView != null) {
mInternalLayout.addView(mExternalView,
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
}
}
public void setOffset(int xOffset, int yOffset, int relativeViewNumber) {
// Scroll the target view relative to its own position relative to
// currently displayed view
mInternalLayout.scrollTo(
getViewOffset(mViewNumber, relativeViewNumber) + xOffset,
yOffset);
}
public int getCurrentOffset() {
// Return the current scroll position
return mInternalLayout.getScrollX();
}
public void requestFocus() {
mInternalLayout.requestFocus();
}
}
private class FlingGalleryAnimation extends Animation {
private boolean mIsAnimationInProgres;
private int mRelativeViewNumber;
private int mInitialOffset;
private int mTargetOffset;
private int mTargetDistance;
public FlingGalleryAnimation() {
mIsAnimationInProgres = false;
mRelativeViewNumber = 0;
mInitialOffset = 0;
mTargetOffset = 0;
mTargetDistance = 0;
}
public void prepareAnimation(int relativeViewNumber) {
// If we are animating relative to a new view
if (mRelativeViewNumber != relativeViewNumber) {
if (mIsAnimationInProgres == true) {
// We only have three views so if requested again to animate
// in same direction we must snap
int newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1
: -1;
int animDirection = (mTargetDistance < 0) ? 1 : -1;
// If animation in same direction
if (animDirection == newDirection) {
// Ran out of time to animate so snap to the target
// offset
mViews[0].setOffset(mTargetOffset, 0,
mRelativeViewNumber);
mViews[1].setOffset(mTargetOffset, 0,
mRelativeViewNumber);
mViews[2].setOffset(mTargetOffset, 0,
mRelativeViewNumber);
}
}
// Set relative view number for animation
mRelativeViewNumber = relativeViewNumber;
}
// Note: In this implementation the targetOffset will always be zero
// as we are centering the view; but we include the calculations of
// targetOffset and targetDistance for use in future implementations
mInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset();
mTargetOffset = getViewOffset(mRelativeViewNumber,
mRelativeViewNumber);
mTargetDistance = mTargetOffset - mInitialOffset;
// Configure base animation properties
this.setDuration(mAnimationDuration);
this.setInterpolator(mDecelerateInterpolater);
// Start/continued animation
mIsAnimationInProgres = true;
}
@Override
protected void applyTransformation(float interpolatedTime,
Transformation transformation) {
// Ensure interpolatedTime does not over-shoot then calculate new
// offset
interpolatedTime = (interpolatedTime > 1.0f) ? 1.0f
: interpolatedTime;
int offset = mInitialOffset
+ (int) (mTargetDistance * interpolatedTime);
for (int viewNumber = 0; viewNumber < 3; viewNumber++) {
// Only need to animate the visible views as the other view will
// always be off-screen
if ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber))
|| (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber))) {
mViews[viewNumber]
.setOffset(offset, 0, mRelativeViewNumber);
}
}
}
@Override
public boolean getTransformation(long currentTime,
Transformation outTransformation) {
if (super.getTransformation(currentTime, outTransformation) == false) {
// Perform final adjustment to offsets to cleanup animation
mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);
mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);
mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber);
// Reached the animation target
mIsAnimationInProgres = false;
return false;
}
// Cancel if the screen touched
if (mIsTouched || mIsDragging) {
// Note that at this point we still consider ourselves to be
// animating
// because we have not yet reached the target offset; its just
// that the
// user has temporarily interrupted the animation with a touch
// gesture
return false;
}
return true;
}
}
private class FlingGestureDetector extends
GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
// Stop animation
mIsTouched = true;
// Reset fling state
mFlingDirection = 0;
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (e2.getAction() == MotionEvent.ACTION_MOVE) {
if (mIsDragging == false) {
// Stop animation
mIsTouched = true;
// Reconfigure scroll
mIsDragging = true;
mFlingDirection = 0;
mScrollTimestamp = System.currentTimeMillis();
mCurrentOffset = mViews[mCurrentViewNumber]
.getCurrentOffset();
}
float maxVelocity = mGalleryWidth
/ (mAnimationDuration / 1000.0f);
long timestampDelta = System.currentTimeMillis()
- mScrollTimestamp;
float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f);
float currentScrollDelta = e1.getX() - e2.getX();
if (currentScrollDelta < maxScrollDelta * -1)
currentScrollDelta = maxScrollDelta * -1;
if (currentScrollDelta > maxScrollDelta)
currentScrollDelta = maxScrollDelta;
int scrollOffset = Math.round(mCurrentOffset
+ currentScrollDelta);
// We can't scroll more than the width of our own frame layout
if (scrollOffset >= mGalleryWidth)
scrollOffset = mGalleryWidth;
if (scrollOffset <= mGalleryWidth * -1)
scrollOffset = mGalleryWidth * -1;
mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber);
mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber);
mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber);
}
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (Math.abs(e1.getY() - e2.getY()) <= swipe_max_off_path) {
if (e2.getX() - e1.getX() > swipe_min_distance
&& Math.abs(velocityX) > swipe_threshold_veloicty) {
movePrevious();
}
if (e1.getX() - e2.getX() > swipe_min_distance
&& Math.abs(velocityX) > swipe_threshold_veloicty) {
moveNext();
}
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
// Finalise scrolling
mFlingDirection = 0;
processGesture();
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Reset fling state
mFlingDirection = 0;
return false;
}
}
}
MainActivity.java
- package com.iaiai.fg;
- import android.app.Activity;
- import android.os.Bundle;
- import android.content.Context;
- import android.graphics.Color;
- import android.util.Log;
- import android.view.Gravity;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.View.OnClickListener;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.CheckBox;
- import android.widget.EditText;
- import android.widget.LinearLayout;
- import android.widget.TableLayout;
- import android.widget.TextView;
- /**
- *
- * <p>
- * Title: MainActivity.java
- * </p>
- * <p>
- * E-Mail: 176291935@qq.com
- * </p>
- * <p>
- * QQ: 176291935
- * </p>
- * <p>
- * Http: iaiai.iteye.com
- * </p>
- * <p>
- * Create time: 2012-7-7 上午10:18:53
- * </p>
- *
- * @author 丸子
- * @version 0.0.1
- */
- public class MainActivity extends Activity {
- private final int color_red = Color.argb(100, 200, 0, 0);
- private final int color_green = Color.argb(100, 0, 200, 0);
- private final int color_blue = Color.argb(100, 0, 0, 200);
- private final int color_yellow = Color.argb(100, 200, 200, 0);
- private final int color_purple = Color.argb(100, 200, 0, 200);
- private final String[] mLabelArray = { "View1", "View2", "View3", "View4",
- "View5" };
- private final int[] mColorArray = { color_red, color_green, color_blue,
- color_yellow, color_purple };
- private FlingGallery mGallery;
- private CheckBox mCheckBox;
- // Note: The following handler is critical to correct function of
- // the FlingGallery class. This enables the FlingGallery class to
- // detect when the motion event has ended by finger being lifted
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mGallery.onGalleryTouchEvent(event);
- }
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mGallery = new FlingGallery(this);
- mGallery.setPaddingWidth(5);
- mGallery.setAdapter(new ArrayAdapter<String>(getApplicationContext(),
- android.R.layout.simple_list_item_1, mLabelArray) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Log.d("111", "count=" + position);
- // if (convertView != null && convertView instanceof
- // GalleryViewItem)
- // {
- // GalleryViewItem galleryView = (GalleryViewItem) convertView;
- //
- // galleryView.mEdit1.setText("");
- // galleryView.mText1.setText(mLabelArray[position]);
- // galleryView.mText1.setBackgroundColor(mColorArray[position]);
- // galleryView.mText2.setText(mLabelArray[position]);
- // galleryView.mText2.setBackgroundColor(mColorArray[position]);
- //
- // Log.d("111", "count="+position);
- //
- // return galleryView;
- //
- // }
- return new GalleryViewItem(getApplicationContext(), position);
- }
- });
- LinearLayout layout = new LinearLayout(getApplicationContext());
- layout.setOrientation(LinearLayout.VERTICAL);
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
- layoutParams.setMargins(10, 10, 10, 10);
- layoutParams.weight = 1.0f;
- layout.addView(mGallery, layoutParams);
- mCheckBox = new CheckBox(getApplicationContext());
- mCheckBox.setText("Gallery is Circular");
- mCheckBox.setText("Gallery is Circular");
- mCheckBox.setPadding(50, 10, 0, 10);
- mCheckBox.setTextSize(30);
- mCheckBox.setChecked(true);
- mCheckBox.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- mGallery.setIsGalleryCircular(mCheckBox.isChecked());
- }
- });
- layout.addView(mCheckBox, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT));
- setContentView(layout);
- }
- private class GalleryViewItem extends TableLayout {
- private EditText mEdit1;
- private TextView mText1;
- private TextView mText2;
- private Button mButton1;
- private Button mButton2;
- public GalleryViewItem(Context context, int position) {
- super(context);
- this.setOrientation(LinearLayout.VERTICAL);
- this.setLayoutParams(new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT));
- mEdit1 = new EditText(context);
- this.addView(mEdit1, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT));
- mText1 = new TextView(context);
- mText1.setText(mLabelArray[position]);
- mText1.setTextSize(30);
- mText1.setGravity(Gravity.LEFT);
- mText1.setBackgroundColor(mColorArray[position]);
- this.addView(mText1, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT));
- mButton1 = new Button(context);
- mButton1.setText("<<");
- mButton1.setGravity(Gravity.LEFT);
- mButton1.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- mGallery.movePrevious();
- }
- });
- this.addView(mButton1, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT));
- mButton2 = new Button(context);
- mButton2.setText(">>");
- mButton2.setGravity(Gravity.RIGHT);
- mButton2.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- mGallery.moveNext();
- }
- });
- this.addView(mButton2, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT));
- mText2 = new TextView(context);
- mText2.setText(mLabelArray[position]);
- mText2.setTextSize(30);
- mText2.setGravity(Gravity.RIGHT);
- mText2.setBackgroundColor(mColorArray[position]);
- this.addView(mText2, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT, 1));
- }
- }
- }


浙公网安备 33010602011771号