android实现uc和墨迹天气那样的左右拖动效果

fmms 13年前
     <pre class="brush:java; toolbar: true; auto-links: false;">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;  public class FlingGalleryActivity 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));    }  } }</pre>    <br />    <h3>FlingGallery<pre class="brush:java; toolbar: true; auto-links: false;">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  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;      }     } }</pre></h3>