先上图吧,下图是左右拖动的过程:   具体代码如下:Fling_Gallery类 [java]  view plain copy
  1. package com.xu81.testflip;  
  2. import android.content.Context;  
  3. import android.view.GestureDetector;  
  4. import android.view.KeyEvent;  
  5. import android.view.MotionEvent;  
  6. import android.view.View;  
  7. import android.view.animation.Animation;  
  8. import android.view.animation.AnimationUtils;  
  9. import android.view.animation.Interpolator;  
  10. import android.view.animation.Transformation;  
  11. import android.widget.Adapter;  
  12. import android.widget.FrameLayout;  
  13. import android.widget.LinearLayout;  
  14.   
  15. // TODO:  
  16.   
  17. // 1. In order to improve performance Cache screen bitmap and use for animation  
  18. // 2. Establish superfluous memory allocations and delay or replace with reused objects  
  19. //    Probably need to make sure we are not allocating objects (strings, etc.) in loops  
  20.   
  21. public class FlingGallery extends FrameLayout  
  22. {  
  23.     // Constants  
  24.       
  25.     private final int swipe_min_distance = 120;  
  26.     private final int swipe_max_off_path = 250;  
  27.     private final int swipe_threshold_veloicty = 400;  
  28.   
  29.     // Properties  
  30.       
  31.     private int mViewPaddingWidth = 0;  
  32.     private int mAnimationDuration = 250;  
  33.     private float mSnapBorderRatio = 0.5f;  
  34.     private boolean mIsGalleryCircular = true;  
  35.   
  36.     // Members  
  37.   
  38.     private int mGalleryWidth = 0;  
  39.     private boolean mIsTouched = false;  
  40.     private boolean mIsDragging = false;  
  41.     private float mCurrentOffset = 0.0f;  
  42.     private long mScrollTimestamp = 0;  
  43.     private int mFlingDirection = 0;  
  44.     private int mCurrentPosition = 0;  
  45.     private int mCurrentViewNumber = 0;  
  46.   
  47.     private Context mContext;  
  48.     private Adapter mAdapter;  
  49.     private FlingGalleryView[] mViews;  
  50.     private FlingGalleryAnimation mAnimation;  
  51.     private GestureDetector mGestureDetector;  
  52.     private Interpolator mDecelerateInterpolater;  
  53.   
  54.     public FlingGallery(Context context)  
  55.     {  
  56.         super(context);  
  57.   
  58.         mContext = context;  
  59.         mAdapter = null;  
  60.           
  61.         mViews = new FlingGalleryView[3];  
  62.         mViews[0] = new FlingGalleryView(0this);  
  63.         mViews[1] = new FlingGalleryView(1this);  
  64.         mViews[2] = new FlingGalleryView(2this);  
  65.   
  66.         mAnimation = new FlingGalleryAnimation();  
  67.         mGestureDetector = new GestureDetector(new FlingGestureDetector());  
  68.         mDecelerateInterpolater = AnimationUtils.loadInterpolator(mContext, android.R.anim.decelerate_interpolator);  
  69.     }  
  70.   
  71.     public void setPaddingWidth(int viewPaddingWidth)  
  72.     {  
  73.         mViewPaddingWidth = viewPaddingWidth;  
  74.     }  
  75.   
  76.     public void setAnimationDuration(int animationDuration)  
  77.     {  
  78.         mAnimationDuration = animationDuration;  
  79.     }  
  80.       
  81.     public void setSnapBorderRatio(float snapBorderRatio)  
  82.     {  
  83.         mSnapBorderRatio = snapBorderRatio;  
  84.     }  
  85.   
  86.     public void setIsGalleryCircular(boolean isGalleryCircular)   
  87.     {  
  88.         if (mIsGalleryCircular != isGalleryCircular)  
  89.         {  
  90.             mIsGalleryCircular = isGalleryCircular;  
  91.       
  92.             if (mCurrentPosition == getFirstPosition())  
  93.             {  
  94.                 // We need to reload the view immediately to the left to change it to circular view or blank  
  95.                 mViews[getPrevViewNumber(mCurrentViewNumber)].recycleView(getPrevPosition(mCurrentPosition));             
  96.             }  
  97.       
  98.             if (mCurrentPosition == getLastPosition())  
  99.             {  
  100.                 // We need to reload the view immediately to the right to change it to circular view or blank  
  101.                 mViews[getNextViewNumber(mCurrentViewNumber)].recycleView(getNextPosition(mCurrentPosition));             
  102.             }  
  103.         }  
  104.     }  
  105.   
  106.     public int getGalleryCount()  
  107.     {  
  108.         return (mAdapter == null) ? 0 : mAdapter.getCount();  
  109.     }  
  110.   
  111.     public int getFirstPosition()  
  112.     {  
  113.         return 0;  
  114.     }  
  115.   
  116.     public int getLastPosition()  
  117.     {  
  118.         return (getGalleryCount() == 0) ? 0 : getGalleryCount() - 1;  
  119.     }  
  120.   
  121.     private int getPrevPosition(int relativePosition)  
  122.     {  
  123.         int prevPosition = relativePosition - 1;  
  124.   
  125.         if (prevPosition < getFirstPosition())  
  126.         {  
  127.             prevPosition = getFirstPosition() - 1;  
  128.   
  129.             if (mIsGalleryCircular == true)  
  130.             {  
  131.                 prevPosition = getLastPosition();  
  132.             }  
  133.         }  
  134.   
  135.         return prevPosition;  
  136.     }  
  137.   
  138.     private int getNextPosition(int relativePosition)  
  139.     {  
  140.         int nextPosition = relativePosition + 1;  
  141.   
  142.         if (nextPosition > getLastPosition())  
  143.         {  
  144.             nextPosition = getLastPosition() + 1;  
  145.   
  146.             if (mIsGalleryCircular == true)  
  147.             {  
  148.                 nextPosition = getFirstPosition();  
  149.             }  
  150.         }  
  151.   
  152.         return nextPosition;  
  153.     }  
  154.   
  155.     private int getPrevViewNumber(int relativeViewNumber)  
  156.     {  
  157.         return (relativeViewNumber == 0) ? 2 : relativeViewNumber - 1;  
  158.     }  
  159.   
  160.     private int getNextViewNumber(int relativeViewNumber)  
  161.     {  
  162.         return (relativeViewNumber == 2) ? 0 : relativeViewNumber + 1;  
  163.     }  
  164.       
  165.     @Override  
  166.     protected void onLayout(boolean changed, int left, int top, int right, int bottom)  
  167.     {  
  168.         super.onLayout(changed, left, top, right, bottom);  
  169.   
  170.         // Calculate our view width  
  171.         mGalleryWidth = right - left;  
  172.   
  173.         if (changed == true)  
  174.         {  
  175.             // Position views at correct starting offsets  
  176.             mViews[0].setOffset(00, mCurrentViewNumber);  
  177.             mViews[1].setOffset(00, mCurrentViewNumber);  
  178.             mViews[2].setOffset(00, mCurrentViewNumber);  
  179.         }  
  180.     }  
  181.   
  182.     public void setAdapter(Adapter adapter)  
  183.     {  
  184.         mAdapter = adapter;  
  185.         mCurrentPosition = 0;  
  186.         mCurrentViewNumber = 0;  
  187.   
  188.         // Load the initial views from adapter  
  189.         mViews[0].recycleView(mCurrentPosition);  
  190.         mViews[1].recycleView(getNextPosition(mCurrentPosition));  
  191.         mViews[2].recycleView(getPrevPosition(mCurrentPosition));  
  192.   
  193.         // Position views at correct starting offsets  
  194.         mViews[0].setOffset(00, mCurrentViewNumber);  
  195.         mViews[1].setOffset(00, mCurrentViewNumber);  
  196.         mViews[2].setOffset(00, mCurrentViewNumber);  
  197.     }  
  198.   
  199.     private int getViewOffset(int viewNumber, int relativeViewNumber)  
  200.     {  
  201.         // Determine width including configured padding width  
  202.         int offsetWidth = mGalleryWidth + mViewPaddingWidth;  
  203.   
  204.         // Position the previous view one measured width to left  
  205.         if (viewNumber == getPrevViewNumber(relativeViewNumber))  
  206.         {  
  207.             return offsetWidth;  
  208.         }  
  209.   
  210.         // Position the next view one measured width to the right  
  211.         if (viewNumber == getNextViewNumber(relativeViewNumber))  
  212.         {  
  213.             return offsetWidth * -1;  
  214.         }  
  215.   
  216.         return 0;  
  217.     }  
  218.   
  219.     void movePrevious()  
  220.     {  
  221.         // Slide to previous view  
  222.         mFlingDirection = 1;  
  223.         processGesture();  
  224.     }  
  225.   
  226.     void moveNext()  
  227.     {  
  228.         // Slide to next view  
  229.         mFlingDirection = -1;  
  230.         processGesture();  
  231.     }  
  232.   
  233.      @Override  
  234.      public boolean onKeyDown(int keyCode, KeyEvent event)  
  235.      {  
  236.         switch (keyCode)  
  237.         {  
  238.         case KeyEvent.KEYCODE_DPAD_LEFT:  
  239.             movePrevious();  
  240.             return true;  
  241.       
  242.         case KeyEvent.KEYCODE_DPAD_RIGHT:  
  243.             moveNext();  
  244.             return true;  
  245.       
  246.         case KeyEvent.KEYCODE_DPAD_CENTER:  
  247.         case KeyEvent.KEYCODE_ENTER:  
  248.         }  
  249.   
  250.         return super.onKeyDown(keyCode, event);  
  251.     }  
  252.   
  253.     public boolean onGalleryTouchEvent(MotionEvent event)  
  254.     {  
  255.         boolean consumed = mGestureDetector.onTouchEvent(event);  
  256.           
  257.         if (event.getAction() == MotionEvent.ACTION_UP)  
  258.         {  
  259.             if (mIsTouched || mIsDragging)  
  260.             {  
  261.                 processScrollSnap();  
  262.                 processGesture();  
  263.             }  
  264.         }  
  265.           
  266.         return consumed;  
  267.     }  
  268.   
  269.     void processGesture()  
  270.     {  
  271.         int newViewNumber = mCurrentViewNumber;  
  272.         int reloadViewNumber = 0;  
  273.         int reloadPosition = 0;  
  274.   
  275.         mIsTouched = false;  
  276.         mIsDragging = false;  
  277.   
  278.         if (mFlingDirection > 0)  
  279.         {  
  280.             if (mCurrentPosition > getFirstPosition() || mIsGalleryCircular == true)  
  281.             {  
  282.                 // Determine previous view and outgoing view to recycle  
  283.                 newViewNumber = getPrevViewNumber(mCurrentViewNumber);  
  284.                 mCurrentPosition = getPrevPosition(mCurrentPosition);  
  285.                 reloadViewNumber = getNextViewNumber(mCurrentViewNumber);   
  286.                 reloadPosition = getPrevPosition(mCurrentPosition);  
  287.             }  
  288.         }  
  289.   
  290.         if (mFlingDirection < 0)  
  291.         {  
  292.             if (mCurrentPosition < getLastPosition() || mIsGalleryCircular == true)  
  293.             {  
  294.                 // Determine the next view and outgoing view to recycle  
  295.                 newViewNumber = getNextViewNumber(mCurrentViewNumber);  
  296.                 mCurrentPosition = getNextPosition(mCurrentPosition);  
  297.                 reloadViewNumber = getPrevViewNumber(mCurrentViewNumber);  
  298.                 reloadPosition = getNextPosition(mCurrentPosition);  
  299.             }  
  300.         }  
  301.   
  302.         if (newViewNumber != mCurrentViewNumber)  
  303.         {  
  304.             mCurrentViewNumber = newViewNumber;   
  305.   
  306.             // Reload outgoing view from adapter in new position  
  307.             mViews[reloadViewNumber].recycleView(reloadPosition);  
  308.         }  
  309.   
  310.         // Ensure input focus on the current view  
  311.         mViews[mCurrentViewNumber].requestFocus();  
  312.   
  313.         // Run the slide animations for view transitions  
  314.         mAnimation.prepareAnimation(mCurrentViewNumber);  
  315.         this.startAnimation(mAnimation);  
  316.   
  317.         // Reset fling state  
  318.         mFlingDirection = 0;  
  319.     }  
  320.   
  321.     void processScrollSnap()  
  322.     {  
  323.         // Snap to next view if scrolled passed snap position  
  324.         float rollEdgeWidth = mGalleryWidth * mSnapBorderRatio;  
  325.         int rollOffset = mGalleryWidth - (int) rollEdgeWidth;  
  326.         int currentOffset = mViews[mCurrentViewNumber].getCurrentOffset();  
  327.   
  328.         if (currentOffset <= rollOffset * -1)  
  329.         {  
  330.             // Snap to previous view  
  331.             mFlingDirection = 1;  
  332.         }  
  333.   
  334.         if (currentOffset >= rollOffset)  
  335.         {  
  336.             // Snap to next view  
  337.             mFlingDirection = -1;  
  338.         }  
  339.     }  
  340.   
  341.     private class FlingGalleryView  
  342.     {  
  343.         private int mViewNumber;  
  344.         private FrameLayout mParentLayout;  
  345.           
  346.         private FrameLayout mInvalidLayout = null;  
  347.         private LinearLayout mInternalLayout = null;  
  348.         private View mExternalView = null;  
  349.   
  350.         public FlingGalleryView(int viewNumber, FrameLayout parentLayout)  
  351.         {  
  352.             mViewNumber = viewNumber;  
  353.             mParentLayout = parentLayout;  
  354.   
  355.             // Invalid layout is used when outside gallery  
  356.             mInvalidLayout = new FrameLayout(mContext);  
  357.             mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams(   
  358.                     LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  
  359.   
  360.             // Internal layout is permanent for duration  
  361.             mInternalLayout = new LinearLayout(mContext);  
  362.             mInternalLayout.setLayoutParams(new LinearLayout.LayoutParams(   
  363.                     LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  
  364.   
  365.             mParentLayout.addView(mInternalLayout);  
  366.         }  
  367.   
  368.         public void recycleView(int newPosition)  
  369.         {  
  370.             if (mExternalView != null)  
  371.             {  
  372.                 mInternalLayout.removeView(mExternalView);  
  373.             }  
  374.   
  375.             if (mAdapter != null)  
  376.             {  
  377.                 if (newPosition >= getFirstPosition() && newPosition <= getLastPosition())  
  378.                 {  
  379.                     mExternalView = mAdapter.getView(newPosition, mExternalView, mInternalLayout);  
  380.                 }  
  381.                 else  
  382.                 {  
  383.                     mExternalView = mInvalidLayout;  
  384.                 }  
  385.             }  
  386.   
  387.             if (mExternalView != null)  
  388.             {  
  389.                 mInternalLayout.addView(mExternalView, new LinearLayout.LayoutParams(   
  390.                     LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  
  391.             }  
  392.         }  
  393.   
  394.         public void setOffset(int xOffset, int yOffset, int relativeViewNumber)  
  395.         {  
  396.             // Scroll the target view relative to its own position relative to currently displayed view  
  397.             mInternalLayout.scrollTo(getViewOffset(mViewNumber, relativeViewNumber) + xOffset, yOffset);  
  398.         }  
  399.           
  400.         public int getCurrentOffset()  
  401.         {  
  402.             // Return the current scroll position  
  403.             return mInternalLayout.getScrollX();  
  404.         }  
  405.   
  406.         public void requestFocus()  
  407.         {  
  408.             mInternalLayout.requestFocus();  
  409.         }  
  410.     }  
  411.   
  412.     private class FlingGalleryAnimation extends Animation  
  413.     {  
  414.         private boolean mIsAnimationInProgres;  
  415.         private int mRelativeViewNumber;  
  416.         private int mInitialOffset;  
  417.         private int mTargetOffset;  
  418.         private int mTargetDistance;      
  419.    
  420.         public FlingGalleryAnimation()  
  421.         {  
  422.             mIsAnimationInProgres = false;  
  423.             mRelativeViewNumber = 0;  
  424.             mInitialOffset = 0;  
  425.             mTargetOffset = 0;  
  426.             mTargetDistance = 0;  
  427.         }  
  428.    
  429.         public void prepareAnimation(int relativeViewNumber)  
  430.         {  
  431.             // If we are animating relative to a new view  
  432.             if (mRelativeViewNumber != relativeViewNumber)  
  433.             {  
  434.                 if (mIsAnimationInProgres == true)  
  435.                 {  
  436.                     // We only have three views so if requested again to animate in same direction we must snap   
  437.                     int newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1 : -1;  
  438.                     int animDirection = (mTargetDistance < 0) ? 1 : -1;   
  439.   
  440.                     // If animation in same direction  
  441.                     if (animDirection == newDirection)  
  442.                     {  
  443.                         // Ran out of time to animate so snap to the target offset  
  444.                         mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);  
  445.                         mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);  
  446.                         mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber);   
  447.                     }  
  448.                 }  
  449.       
  450.                 // Set relative view number for animation  
  451.                 mRelativeViewNumber = relativeViewNumber;  
  452.             }  
  453.   
  454.             // Note: In this implementation the targetOffset will always be zero  
  455.             // as we are centering the view; but we include the calculations of  
  456.             // targetOffset and targetDistance for use in future implementations  
  457.   
  458.             mInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset();  
  459.             mTargetOffset = getViewOffset(mRelativeViewNumber, mRelativeViewNumber);  
  460.             mTargetDistance = mTargetOffset - mInitialOffset;  
  461.   
  462.             // Configure base animation properties  
  463.             this.setDuration(mAnimationDuration);  
  464.             this.setInterpolator(mDecelerateInterpolater);  
  465.   
  466.             // Start/continued animation  
  467.             mIsAnimationInProgres = true;  
  468.         }  
  469.   
  470.         @Override  
  471.         protected void applyTransformation(float interpolatedTime, Transformation transformation)  
  472.         {  
  473.             // Ensure interpolatedTime does not over-shoot then calculate new offset  
  474.             interpolatedTime = (interpolatedTime > 1.0f) ? 1.0f : interpolatedTime;  
  475.             int offset = mInitialOffset + (int) (mTargetDistance * interpolatedTime);  
  476.   
  477.             for (int viewNumber = 0; viewNumber < 3; viewNumber++)  
  478.             {  
  479.                 // Only need to animate the visible views as the other view will always be off-screen  
  480.                 if ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber)) ||  
  481.                     (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber)))  
  482.                 {  
  483.                     mViews[viewNumber].setOffset(offset, 0, mRelativeViewNumber);  
  484.                 }  
  485.             }  
  486.         }  
  487.   
  488.         @Override  
  489.         public boolean getTransformation(long currentTime, Transformation outTransformation)  
  490.         {  
  491.             if (super.getTransformation(currentTime, outTransformation) == false)  
  492.             {  
  493.                 // Perform final adjustment to offsets to cleanup animation  
  494.                 mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);  
  495.                 mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);  
  496.                 mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber);  
  497.   
  498.                 // Reached the animation target  
  499.                 mIsAnimationInProgres = false;  
  500.   
  501.                 return false;  
  502.             }  
  503.    
  504.             // Cancel if the screen touched  
  505.             if (mIsTouched || mIsDragging)  
  506.             {  
  507.                 // Note that at this point we still consider ourselves to be animating  
  508.                 // because we have not yet reached the target offset; its just that the  
  509.                 // user has temporarily interrupted the animation with a touch gesture  
  510.   
  511.                 return false;  
  512.             }  
  513.   
  514.             return true;  
  515.         }  
  516.     }  
  517.   
  518.     private class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener  
  519.     {  
  520.         @Override  
  521.         public boolean onDown(MotionEvent e)  
  522.         {  
  523.             // Stop animation  
  524.             mIsTouched = true;  
  525.   
  526.             // Reset fling state  
  527.             mFlingDirection = 0;  
  528.             return true;  
  529.         }  
  530.   
  531.         @Override  
  532.         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)  
  533.         {  
  534.             if (e2.getAction() == MotionEvent.ACTION_MOVE)  
  535.             {  
  536.                 if (mIsDragging == false)  
  537.                 {  
  538.                     // Stop animation  
  539.                     mIsTouched = true;  
  540.        
  541.                     // Reconfigure scroll  
  542.                     mIsDragging = true;  
  543.                     mFlingDirection = 0;  
  544.                     mScrollTimestamp = System.currentTimeMillis();  
  545.                     mCurrentOffset = mViews[mCurrentViewNumber].getCurrentOffset();  
  546.                 }  
  547.   
  548.                 float maxVelocity = mGalleryWidth / (mAnimationDuration / 1000.0f);  
  549.                 long timestampDelta = System.currentTimeMillis() - mScrollTimestamp;  
  550.                 float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f);   
  551.                 float currentScrollDelta = e1.getX() - e2.getX();  
  552.   
  553.                 if (currentScrollDelta < maxScrollDelta * -1) currentScrollDelta = maxScrollDelta * -1;  
  554.                 if (currentScrollDelta > maxScrollDelta) currentScrollDelta = maxScrollDelta;  
  555.                 int scrollOffset = Math.round(mCurrentOffset + currentScrollDelta);  
  556.   
  557.                 // We can't scroll more than the width of our own frame layout  
  558.                 if (scrollOffset >= mGalleryWidth) scrollOffset = mGalleryWidth;  
  559.                 if (scrollOffset <= mGalleryWidth * -1) scrollOffset = mGalleryWidth * -1;  
  560.                   
  561.                 mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber);  
  562.                 mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber);  
  563.                 mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber);  
  564.             }  
  565.   
  566.             return false;  
  567.         }  
  568.   
  569.         @Override  
  570.         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)  
  571.         {  
  572.             if (Math.abs(e1.getY() - e2.getY()) <= swipe_max_off_path)  
  573.             {  
  574.                 if (e2.getX() - e1.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty)  
  575.                 {  
  576.                     movePrevious();  
  577.                 }  
  578.   
  579.                 if(e1.getX() - e2.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty)  
  580.                 {  
  581.                     moveNext();  

更多相关文章

  1. Android WindowManager 全局悬浮窗 + AccessibilityService+ Rec
  2. Android实现View的任意拖动
  3. Android基于多触控的图片缩放和拖动代码实现
  4. 自定义Seekbar拖动条样式
  5. Android自定义Seekbar拖动条式样
  6. android中SeekBar拖动进度条的使用及事件监听
  7. Android 小項目之---Iphone拖动图片特效 (附源码)
  8. Android图片左右切换和拖动大小
  9. [置顶] 我的Android进阶之旅------>Android之拖动条(SeekBar和Ra

随机推荐

  1. 如何快速修改MySQL用户的host属性
  2. mysql居然还能实现分布式锁的方法
  3. MySQL中使用流式查询避免数据OOM
  4. 浅谈为什么MySQL不建议delete删除数据
  5. Mysql连接数设置和获取的方法
  6. Mysql中索引和约束的示例语句
  7. sqoop export导出 map100% reduce0% 卡住
  8. mysql查询条件not in 和 in的区别及原因
  9. 解决mysql使用not in 包含null值的问题
  10. 解决从集合运算到mysql的not like找不出N