视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
android左右滑动
2025-09-29 00:16:01 责编:小OO
文档
import java.util.HashSet;

import java.util.Set;

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;

public class FlingGallery extends FrameLayout

{

private Set listeners;

private final int swipe_min_distance = 120;

private final int swipe_max_off_path = 250;

private final int swipe_threshold_veloicty = 400;

private int mViewPaddingWidth = 0;

private int mAnimationDuration = 250;

private float mSnapBorderRatio = 0.5f;

private boolean mIsGalleryCircular = true;

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

listeners = new HashSet();

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 addGalleryChangeListener(OnGalleryChangeListener listener){

listeners.add(listener);

}

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();

}

}

NotifyGalleryChange();

return prevPosition;

}

private int getNextPosition(int relativePosition)

{

int nextPosition = relativePosition + 1;

if (nextPosition > getLastPosition())

{

nextPosition = getLastPosition() + 1;

if (mIsGalleryCircular == true)

{

nextPosition = getFirstPosition();

}

}

NotifyGalleryChange();

return nextPosition;

}

//

private void NotifyGalleryChange() {

for (OnGalleryChangeListener listener :listeners) {

listener.onGalleryChange(mCurrentPosition);

}

}

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)

{

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

mParen

tLayout = parentLayout;

// Invalid layout is used when outside gallery

mInvalidLayout = new FrameLayout(mContext);

mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams(

LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

// Internal layout is permanent for duration

mInternalLayout = new LinearLayout(mContext);

mInternalLayout.setLayoutParams(new LinearLayout.LayoutParams(

LayoutParams.FILL_PARENT, LayoutParams.FILL_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.FILL_PARENT, LayoutParams.FILL_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 scro

ll

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;

}

}

public GestureDetector getMGestureDetector() {

return mGestureDetector;

}

}下载本文

显示全文
专题