首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > Android >

Android中兑现左右滑动View

2012-06-28 
Android中实现左右滑动Viewiphone中有很多应用都能够左右滑动,非常cool,关键是实现起来非常简单。android比

Android中实现左右滑动View
iphone中有很多应用都能够左右滑动,非常cool,关键是实现起来非常简单。android比起来就差远了,网上有不少帖子。 我在这边重新分享下自己的经验吧。

运行效果:



AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.iaiai.test.gallery" android:versionCode="1"android:versionName="1.0"><application android:icon="@drawable/icon" android:label="@string/app_name"><activity android:name=".TestGallery" android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application><uses-sdk android:minSdkVersion="4" /></manifest> 


TestGallery.java
package com.iaiai.test.gallery;import android.app.Activity;import android.graphics.Color;import android.os.Bundle;import android.util.Log;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.ViewGroup.LayoutParams;import android.widget.ArrayAdapter;import android.widget.LinearLayout;import android.widget.TextView;import com.iaiai.test.view.FlingGallery;import com.iaiai.test.view.OnGalleryChangeListener;/** *  * <p> * Title: TestGallery.java * </p> * <p> * E-Mail: 176291935@qq.com * </p> * <p> * QQ: 176291935 * </p> * <p> * Http: iaiai.iteye.com * </p> * <p> * Create time: 2011-9-29 * </p> *  * @author 丸子 * @version 0.0.1 */public class TestGallery 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 FlingGallery gallery;private final String[] mLabelArray = { "View1", "View2", "View3", "View4","View5" };private final int[] mColorArray = { color_red, color_green, color_blue,color_yellow, color_purple };/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);gallery = new FlingGallery(this); gallery.setPaddingWidth(5);// gallery.setBackgroundColor(Color.WHITE);gallery.setAdapter(new ArrayAdapter<String>(getApplicationContext(),android.R.layout.simple_list_item_1, mLabelArray) {@Overridepublic View getView(int position, View convertView, ViewGroup parent) {LinearLayout view = new LinearLayout(getApplicationContext());view.setBackgroundColor(mColorArray[position]);view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));TextView label = new TextView(getApplicationContext());label.setText(mLabelArray[position]);label.setTextSize(24);label.setTextColor(Color.WHITE);label.setGravity(Gravity.CENTER);view.addView(label, LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT);return view;}});gallery.addGalleryChangeListener(new OnGalleryChangeListener() {                @Override              public void onGalleryChange(int currentItem) {                  // 干些想干的事件              Log.v("m","currentItem:"+currentItem);            }            });  setContentView(gallery);}public boolean onTouchEvent(MotionEvent event) {return gallery.onGalleryTouchEvent(event);}}


FlingGallery.java
package com.iaiai.test.view;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;/** *  * <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: 2011-9-29 * </p> *  * @author 丸子 * @version 0.0.1 */public class FlingGallery extends FrameLayout {private Set<OnGalleryChangeListener> 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<OnGalleryChangeListener>();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 blankmViews[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 blankmViews[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;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {super.onLayout(changed, left, top, right, bottom);// Calculate our view widthmGalleryWidth = right - left;if (changed) {// Position views at correct starting offsetsmViews[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 adaptermViews[0].recycleView(mCurrentPosition);mViews[1].recycleView(getNextPosition(mCurrentPosition));mViews[2].recycleView(getPrevPosition(mCurrentPosition));// Position views at correct starting offsetsmViews[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 widthint offsetWidth = mGalleryWidth + mViewPaddingWidth;// Position the previous view one measured width to leftif (viewNumber == getPrevViewNumber(relativeViewNumber)) {return offsetWidth;}// Position the next view one measured width to the rightif (viewNumber == getNextViewNumber(relativeViewNumber)) {return offsetWidth * -1;}return 0;}void movePrevious() {// Slide to previous viewmFlingDirection = 1;processGesture();}void moveNext() {// Slide to next viewmFlingDirection = -1;processGesture();}@Overridepublic 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 recyclenewViewNumber = 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 recyclenewViewNumber = getNextViewNumber(mCurrentViewNumber);mCurrentPosition = getNextPosition(mCurrentPosition);reloadViewNumber = getPrevViewNumber(mCurrentViewNumber);reloadPosition = getNextPosition(mCurrentPosition);}}if (newViewNumber != mCurrentViewNumber) {mCurrentViewNumber = newViewNumber;// Reload outgoing view from adapter in new positionmViews[reloadViewNumber].recycleView(reloadPosition);}// Ensure input focus on the current viewmViews[mCurrentViewNumber].requestFocus();// Run the slide animations for view transitionsmAnimation.prepareAnimation(mCurrentViewNumber);this.startAnimation(mAnimation);// Reset fling statemFlingDirection = 0;}void processScrollSnap() {// Snap to next view if scrolled passed snap positionfloat rollEdgeWidth = mGalleryWidth * mSnapBorderRatio;int rollOffset = mGalleryWidth - (int) rollEdgeWidth;int currentOffset = mViews[mCurrentViewNumber].getCurrentOffset();if (currentOffset <= rollOffset * -1) {// Snap to previous viewmFlingDirection = 1;}if (currentOffset >= rollOffset) {// Snap to next viewmFlingDirection = -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 gallerymInvalidLayout = new FrameLayout(mContext);mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));// Internal layout is permanent for durationmInternalLayout = 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 viewmInternalLayout.scrollTo(getViewOffset(mViewNumber, relativeViewNumber) + xOffset,yOffset);}public int getCurrentOffset() {// Return the current scroll positionreturn 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 viewif (mRelativeViewNumber != relativeViewNumber) {if (mIsAnimationInProgres == true) {// We only have three views so if requested again to animate// in same direction we must snapint newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1: -1;int animDirection = (mTargetDistance < 0) ? 1 : -1;// If animation in same directionif (animDirection == newDirection) {// Ran out of time to animate so snap to the target// offsetmViews[0].setOffset(mTargetOffset, 0,mRelativeViewNumber);mViews[1].setOffset(mTargetOffset, 0,mRelativeViewNumber);mViews[2].setOffset(mTargetOffset, 0,mRelativeViewNumber);}}// Set relative view number for animationmRelativeViewNumber = 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 implementationsmInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset();mTargetOffset = getViewOffset(mRelativeViewNumber,mRelativeViewNumber);mTargetDistance = mTargetOffset - mInitialOffset;// Configure base animation propertiesthis.setDuration(mAnimationDuration);this.setInterpolator(mDecelerateInterpolater);// Start/continued animationmIsAnimationInProgres = true;}@Overrideprotected void applyTransformation(float interpolatedTime,Transformation transformation) {// Ensure interpolatedTime does not over-shoot then calculate new// offsetinterpolatedTime = (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-screenif ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber))|| (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber))) {mViews[viewNumber].setOffset(offset, 0, mRelativeViewNumber);}}}@Overridepublic boolean getTransformation(long currentTime,Transformation outTransformation) {if (super.getTransformation(currentTime, outTransformation) == false) {// Perform final adjustment to offsets to cleanup animationmViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber);// Reached the animation targetmIsAnimationInProgres = false;return false;}// Cancel if the screen touchedif (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// gesturereturn false;}return true;}}private class FlingGestureDetector extendsGestureDetector.SimpleOnGestureListener {@Overridepublic boolean onDown(MotionEvent e) {// Stop animationmIsTouched = true;// Reset fling statemFlingDirection = 0;return true;}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {if (e2.getAction() == MotionEvent.ACTION_MOVE) {if (mIsDragging == false) {// Stop animationmIsTouched = true;// Reconfigure scrollmIsDragging = 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 layoutif (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;}@Overridepublic 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;}@Overridepublic void onLongPress(MotionEvent e) {// Finalise scrollingmFlingDirection = 0;processGesture();}@Overridepublic void onShowPress(MotionEvent e) {}@Overridepublic boolean onSingleTapUp(MotionEvent e) {// Reset fling statemFlingDirection = 0;return false;}}public GestureDetector getMGestureDetector() {return mGestureDetector;}}


OnGalleryChangeListener.java
package com.iaiai.test.view;/** *  * <p> * Title: OnGalleryChangeListener.java * </p> * <p> * E-Mail: 176291935@qq.com * </p> * <p> * QQ: 176291935 * </p> * <p> * Http: iaiai.iteye.com * </p> * <p> * Create time: 2011-9-29 * </p> *  * @author 丸子 * @version 0.0.1 */public interface OnGalleryChangeListener {public void onGalleryChange(int currentItem);}

热点排行