android左右滑动实现activity切换类 (整合代码实例),Android左右滑动实现Activity切换类 (整合代码实例)...

packagecom.xu81.testflip;

importandroid.content.Context;

importandroid.view.GestureDetector;

importandroid.view.KeyEvent;

importandroid.view.MotionEvent;

importandroid.view.View;

importandroid.view.animation.Animation;

importandroid.view.animation.AnimationUtils;

importandroid.view.animation.Interpolator;

importandroid.view.animation.Transformation;

importandroid.widget.Adapter;

importandroid.widget.FrameLayout;

importandroid.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

publicclassFlingGalleryextendsFrameLayout

{

// Constants

privatefinalintswipe_min_distance =120;

privatefinalintswipe_max_off_path =250;

privatefinalintswipe_threshold_veloicty =400;

// Properties

privateintmViewPaddingWidth =0;

privateintmAnimationDuration =250;

privatefloatmSnapBorderRatio =0.5f;

privatebooleanmIsGalleryCircular =true;

// Members

privateintmGalleryWidth =0;

privatebooleanmIsTouched =false;

privatebooleanmIsDragging =false;

privatefloatmCurrentOffset =0.0f;

privatelongmScrollTimestamp =0;

privateintmFlingDirection =0;

privateintmCurrentPosition =0;

privateintmCurrentViewNumber =0;

privateContext mContext;

privateAdapter mAdapter;

privateFlingGalleryView[] mViews;

privateFlingGalleryAnimation mAnimation;

privateGestureDetector mGestureDetector;

privateInterpolator mDecelerateInterpolater;

publicFlingGallery(Context context)

{

super(context);

mContext = context;

mAdapter = null;

mViews = newFlingGalleryView[3];

mViews[0] =newFlingGalleryView(0,this);

mViews[1] =newFlingGalleryView(1,this);

mViews[2] =newFlingGalleryView(2,this);

mAnimation = newFlingGalleryAnimation();

mGestureDetector = newGestureDetector(newFlingGestureDetector());

mDecelerateInterpolater = AnimationUtils.loadInterpolator(mContext, android.R.anim.decelerate_interpolator);

}

publicvoidsetPaddingWidth(intviewPaddingWidth)

{

mViewPaddingWidth = viewPaddingWidth;

}

publicvoidsetAnimationDuration(intanimationDuration)

{

mAnimationDuration = animationDuration;

}

publicvoidsetSnapBorderRatio(floatsnapBorderRatio)

{

mSnapBorderRatio = snapBorderRatio;

}

publicvoidsetIsGalleryCircular(booleanisGalleryCircular)

{

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

}

}

}

publicintgetGalleryCount()

{

return(mAdapter ==null) ?0: mAdapter.getCount();

}

publicintgetFirstPosition()

{

return0;

}

publicintgetLastPosition()

{

return(getGalleryCount() ==0) ?0: getGalleryCount() -1;

}

privateintgetPrevPosition(intrelativePosition)

{

intprevPosition = relativePosition -1;

if(prevPosition 

{

prevPosition = getFirstPosition() - 1;

if(mIsGalleryCircular ==true)

{

prevPosition = getLastPosition();

}

}

returnprevPosition;

}

privateintgetNextPosition(intrelativePosition)

{

intnextPosition = relativePosition +1;

if(nextPosition > getLastPosition())

{

nextPosition = getLastPosition() + 1;

if(mIsGalleryCircular ==true)

{

nextPosition = getFirstPosition();

}

}

returnnextPosition;

}

privateintgetPrevViewNumber(intrelativeViewNumber)

{

return(relativeViewNumber ==0) ?2: relativeViewNumber -1;

}

privateintgetNextViewNumber(intrelativeViewNumber)

{

return(relativeViewNumber ==2) ?0: relativeViewNumber +1;

}

@Override

protectedvoidonLayout(booleanchanged,intleft,inttop,intright,intbottom)

{

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

}

}

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

}

privateintgetViewOffset(intviewNumber,intrelativeViewNumber)

{

// Determine width including configured padding width

intoffsetWidth = mGalleryWidth + mViewPaddingWidth;

// Position the previous view one measured width to left

if(viewNumber == getPrevViewNumber(relativeViewNumber))

{

returnoffsetWidth;

}

// Position the next view one measured width to the right

if(viewNumber == getNextViewNumber(relativeViewNumber))

{

returnoffsetWidth * -1;

}

return0;

}

voidmovePrevious()

{

// Slide to previous view

mFlingDirection = 1;

processGesture();

}

voidmoveNext()

{

// Slide to next view

mFlingDirection = -1;

processGesture();

}

@Override

publicbooleanonKeyDown(intkeyCode, KeyEvent event)

{

switch(keyCode)

{

caseKeyEvent.KEYCODE_DPAD_LEFT:

movePrevious();

returntrue;

caseKeyEvent.KEYCODE_DPAD_RIGHT:

moveNext();

returntrue;

caseKeyEvent.KEYCODE_DPAD_CENTER:

caseKeyEvent.KEYCODE_ENTER:

}

returnsuper.onKeyDown(keyCode, event);

}

publicbooleanonGalleryTouchEvent(MotionEvent event)

{

booleanconsumed = mGestureDetector.onTouchEvent(event);

if(event.getAction() == MotionEvent.ACTION_UP)

{

if(mIsTouched || mIsDragging)

{

processScrollSnap();

processGesture();

}

}

returnconsumed;

}

voidprocessGesture()

{

intnewViewNumber = mCurrentViewNumber;

intreloadViewNumber =0;

intreloadPosition =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 

{

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

}

voidprocessScrollSnap()

{

// Snap to next view if scrolled passed snap position

floatrollEdgeWidth = mGalleryWidth * mSnapBorderRatio;

introllOffset = mGalleryWidth - (int) rollEdgeWidth;

intcurrentOffset = mViews[mCurrentViewNumber].getCurrentOffset();

if(currentOffset <= rollOffset * -1)

{

// Snap to previous view

mFlingDirection = 1;

}

if(currentOffset >= rollOffset)

{

// Snap to next view

mFlingDirection = -1;

}

}

privateclassFlingGalleryView

{

privateintmViewNumber;

privateFrameLayout mParentLayout;

privateFrameLayout mInvalidLayout =null;

privateLinearLayout mInternalLayout =null;

privateView mExternalView =null;

publicFlingGalleryView(intviewNumber, FrameLayout parentLayout)

{

mViewNumber = viewNumber;

mParentLayout = parentLayout;

// Invalid layout is used when outside gallery

mInvalidLayout = newFrameLayout(mContext);

mInvalidLayout.setLayoutParams(newLinearLayout.LayoutParams(

LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

// Internal layout is permanent for duration

mInternalLayout = newLinearLayout(mContext);

mInternalLayout.setLayoutParams(newLinearLayout.LayoutParams(

LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

mParentLayout.addView(mInternalLayout);

}

publicvoidrecycleView(intnewPosition)

{

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, newLinearLayout.LayoutParams(

LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

}

}

publicvoidsetOffset(intxOffset,intyOffset,intrelativeViewNumber)

{

// Scroll the target view relative to its own position relative to currently displayed view

mInternalLayout.scrollTo(getViewOffset(mViewNumber, relativeViewNumber) + xOffset, yOffset);

}

publicintgetCurrentOffset()

{

// Return the current scroll position

returnmInternalLayout.getScrollX();

}

publicvoidrequestFocus()

{

mInternalLayout.requestFocus();

}

}

privateclassFlingGalleryAnimationextendsAnimation

{

privatebooleanmIsAnimationInProgres;

privateintmRelativeViewNumber;

privateintmInitialOffset;

privateintmTargetOffset;

privateintmTargetDistance;

publicFlingGalleryAnimation()

{

mIsAnimationInProgres = false;

mRelativeViewNumber = 0;

mInitialOffset = 0;

mTargetOffset = 0;

mTargetDistance = 0;

}

publicvoidprepareAnimation(intrelativeViewNumber)

{

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

intnewDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ?1: -1;

intanimDirection = (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

protectedvoidapplyTransformation(floatinterpolatedTime, Transformation transformation)

{

// Ensure interpolatedTime does not over-shoot then calculate new offset

interpolatedTime = (interpolatedTime > 1.0f) ?1.0f : interpolatedTime;

intoffset = mInitialOffset + (int) (mTargetDistance * interpolatedTime);

for(intviewNumber =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 

{

mViews[viewNumber].setOffset(offset, 0, mRelativeViewNumber);

}

}

}

@Override

publicbooleangetTransformation(longcurrentTime, 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;

returnfalse;

}

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

returnfalse;

}

returntrue;

}

}

privateclassFlingGestureDetectorextendsGestureDetector.SimpleOnGestureListener

{

@Override

publicbooleanonDown(MotionEvent e)

{

// Stop animation

mIsTouched = true;

// Reset fling state

mFlingDirection = 0;

returntrue;

}

@Override

publicbooleanonScroll(MotionEvent e1, MotionEvent e2,floatdistanceX,floatdistanceY)

{

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

}

floatmaxVelocity = mGalleryWidth / (mAnimationDuration /1000.0f);

longtimestampDelta = System.currentTimeMillis() - mScrollTimestamp;

floatmaxScrollDelta = maxVelocity * (timestampDelta /1000.0f);

floatcurrentScrollDelta = e1.getX() - e2.getX();

if(currentScrollDelta 

if(currentScrollDelta > maxScrollDelta) currentScrollDelta = maxScrollDelta;

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

}

returnfalse;

}

@Override

publicbooleanonFling(MotionEvent e1, MotionEvent e2,floatvelocityX,floatvelocityY)

{

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

}

}

returnfalse;

}

@Override

publicvoidonLongPress(MotionEvent e)

{

// Finalise scrolling

mFlingDirection = 0;

processGesture();

}

@Override

publicvoidonShowPress(MotionEvent e)

{

}

@Override

publicbooleanonSingleTapUp(MotionEvent e)

{

// Reset fling state

mFlingDirection = 0;

returnfalse;

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值