首页 > 系统 > Android > 正文

android自定义ViewPager水平滑动弹性效果

2019-10-22 18:18:46
字体:
来源:转载
供稿:网友

android ViewPager是一个经常要用到的组件,但android系统本身为我们提供的ViewPager是没有任何效果的,只能是一页一页的滑动,这样会让人感觉很死板,在看一些知名大公司的App时,看到了他们的ViewPager在滑动到最开始或者最后的时候是有一个弹性效果的,使用起来感觉非常的好,于是乎就是百度搜了一下,在StackOverflow中看到一篇文章就是讲如何实现这个效果的。

先看下效果图:滑动到最后一页时仍然可以拉动……

android,ViewPager,水平滑动,弹性,ViewPager水平滑动,ViewPager水平滑动弹性

代码如下:

package com.example.myviewpager;  import android.content.Context; import android.content.res.TypedArray; import android.graphics.Camera; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation;  import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.Animator.AnimatorListener; import com.nineoldandroids.animation.ObjectAnimator;  public class BounceBackViewPager extends ViewPager {   /**   * maximum z distance to translate child view   */  final static int DEFAULT_OVERSCROLL_TRANSLATION = 500;   /**   * duration of overscroll animation in ms   */  final private static int DEFAULT_OVERSCROLL_ANIMATION_DURATION = 400;   @SuppressWarnings("unused")  private final static String DEBUG_TAG = ViewPager.class.getSimpleName();  private final static int INVALID_POINTER_ID = -1;   /**   *   * @author renard, extended by Piotr Zawadzki   *   */  private class OverscrollEffect  {   private float mOverscroll;   private Animator mAnimator;    /**    * @param deltaDistance [0..1] 0->no overscroll, 1>full overscroll    */   public void setPull(final float deltaDistance)   {    mOverscroll = deltaDistance;    invalidateVisibleChilds(mLastPosition);   }    /**    * called when finger is released. starts to animate back to default position    */   private void onRelease()   {    if (mAnimator != null && mAnimator.isRunning())    {     mAnimator.addListener(new AnimatorListener()     {       @Override      public void onAnimationStart(Animator animation)      {      }       @Override      public void onAnimationRepeat(Animator animation)      {      }       @Override      public void onAnimationEnd(Animator animation)      {       startAnimation(0);      }       @Override      public void onAnimationCancel(Animator animation)      {      }     });     mAnimator.cancel();    }    else    {     startAnimation(0);    }   }    private void startAnimation(final float target)   {    mAnimator = ObjectAnimator.ofFloat(this, "pull", mOverscroll, target);    mAnimator.setInterpolator(new DecelerateInterpolator());    final float scale = Math.abs(target - mOverscroll);    mAnimator.setDuration((long) (mOverscrollAnimationDuration * scale));    mAnimator.start();   }    private boolean isOverscrolling()   {    if (mScrollPosition == 0 && mOverscroll < 0)    {     return true;    }    final boolean isLast = (getAdapter().getCount() - 1) == mScrollPosition;    if (isLast && mOverscroll > 0)    {     return true;    }    return false;   }   }   final private OverscrollEffect mOverscrollEffect = new OverscrollEffect();  final private Camera mCamera = new Camera();   private OnPageChangeListener mScrollListener;  private float mLastMotionX;  private int mActivePointerId;  private int mScrollPosition;  private float mScrollPositionOffset;  final private int mTouchSlop;   private float mOverscrollTranslation;  private int mOverscrollAnimationDuration;   public BounceBackViewPager(Context context, AttributeSet attrs)  {   super(context, attrs);   setStaticTransformationsEnabled(true);   final ViewConfiguration configuration = ViewConfiguration.get(context);   mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);   super.setOnPageChangeListener(new MyOnPageChangeListener());   init(attrs);  }   private void init(AttributeSet attrs)  {   TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BounceBackViewPager);   mOverscrollTranslation = a.getDimension(R.styleable.BounceBackViewPager_overscroll_translation_bounce,     DEFAULT_OVERSCROLL_TRANSLATION);   mOverscrollAnimationDuration = a.getInt(R.styleable.BounceBackViewPager_overscroll_animation_duration_bounce,     DEFAULT_OVERSCROLL_ANIMATION_DURATION);   a.recycle();  }   public int getOverscrollAnimationDuration()  {   return mOverscrollAnimationDuration;  }   public void setOverscrollAnimationDuration(int mOverscrollAnimationDuration)  {   this.mOverscrollAnimationDuration = mOverscrollAnimationDuration;  }   public float getOverscrollTranslation()  {   return mOverscrollTranslation;  }   public void setOverscrollTranslation(int mOverscrollTranslation)  {   this.mOverscrollTranslation = mOverscrollTranslation;  }   @Override  public void setOnPageChangeListener(OnPageChangeListener listener)  {   mScrollListener = listener;  };   private void invalidateVisibleChilds(final int position)  {   for (int i = 0; i < getChildCount(); i++)   {    getChildAt(i).invalidate();    }   // this.invalidate();   // final View child = getChildAt(position);   // final View previous = getChildAt(position - 1);   // final View next = getChildAt(position + 1);   // if (child != null) {   // child.invalidate();   // }   // if (previous != null) {   // previous.invalidate();   // }   // if (next != null) {   // next.invalidate();   // }  }   private int mLastPosition = 0;   private class MyOnPageChangeListener implements OnPageChangeListener  {    @Override   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)   {    if (mScrollListener != null)    {     mScrollListener.onPageScrolled(position, positionOffset, positionOffsetPixels);    }    mScrollPosition = position;    mScrollPositionOffset = positionOffset;    mLastPosition = position;    invalidateVisibleChilds(position);   }    @Override   public void onPageSelected(int position)   {     if (mScrollListener != null)    {     mScrollListener.onPageSelected(position);    }   }    @Override   public void onPageScrollStateChanged(final int state)   {     if (mScrollListener != null)    {     mScrollListener.onPageScrollStateChanged(state);    }    if (state == SCROLL_STATE_IDLE)    {     mScrollPositionOffset = 0;    }   }  }   @Override  public boolean onInterceptTouchEvent(MotionEvent ev)  {   try   {    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;    switch (action)    {    case MotionEvent.ACTION_DOWN:    {     mLastMotionX = ev.getX();     mActivePointerId = MotionEventCompat.getPointerId(ev, 0);     break;    }    case MotionEventCompat.ACTION_POINTER_DOWN:    {     final int index = MotionEventCompat.getActionIndex(ev);     final float x = MotionEventCompat.getX(ev, index);     mLastMotionX = x;     mActivePointerId = MotionEventCompat.getPointerId(ev, index);     break;    }    }    return super.onInterceptTouchEvent(ev);   }   catch (IllegalArgumentException e)   {    e.printStackTrace();    return false;   }   catch (ArrayIndexOutOfBoundsException e)   {    e.printStackTrace();    return false;   }  }   @Override  public boolean dispatchTouchEvent(MotionEvent ev)  {   try   {    return super.dispatchTouchEvent(ev);   }   catch (IllegalArgumentException e)   {    e.printStackTrace();    return false;   }   catch (ArrayIndexOutOfBoundsException e)   {    e.printStackTrace();    return false;   }  }   @Override  public boolean onTouchEvent(MotionEvent ev)  {   boolean callSuper = false;    final int action = ev.getAction();   switch (action)   {   case MotionEvent.ACTION_DOWN:   {    callSuper = true;    mLastMotionX = ev.getX();    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);    break;   }   case MotionEventCompat.ACTION_POINTER_DOWN:   {    callSuper = true;    final int index = MotionEventCompat.getActionIndex(ev);    final float x = MotionEventCompat.getX(ev, index);    mLastMotionX = x;    mActivePointerId = MotionEventCompat.getPointerId(ev, index);    break;   }   case MotionEvent.ACTION_MOVE:   {    if (mActivePointerId != INVALID_POINTER_ID)    {     // Scroll to follow the motion event     final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);     final float x = MotionEventCompat.getX(ev, activePointerIndex);     final float deltaX = mLastMotionX - x;     final float oldScrollX = getScrollX();     final int width = getWidth();     final int widthWithMargin = width + getPageMargin();     final int lastItemIndex = getAdapter().getCount() - 1;     final int currentItemIndex = getCurrentItem();     final float leftBound = Math.max(0, (currentItemIndex - 1) * widthWithMargin);     final float rightBound = Math.min(currentItemIndex + 1, lastItemIndex) * widthWithMargin;     final float scrollX = oldScrollX + deltaX;     if (mScrollPositionOffset == 0)     {      if (scrollX < leftBound)      {       if (leftBound == 0)       {        final float over = deltaX + mTouchSlop;        mOverscrollEffect.setPull(over / width);       }      }      else if (scrollX > rightBound)      {       if (rightBound == lastItemIndex * widthWithMargin)       {        final float over = scrollX - rightBound - mTouchSlop;        mOverscrollEffect.setPull(over / width);       }      }     }     else     {      mLastMotionX = x;     }    }    else    {     mOverscrollEffect.onRelease();    }    break;   }   case MotionEvent.ACTION_UP:   case MotionEvent.ACTION_CANCEL:   {    callSuper = true;    mActivePointerId = INVALID_POINTER_ID;    mOverscrollEffect.onRelease();    break;   }   case MotionEvent.ACTION_POINTER_UP:   {    final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;    final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);    if (pointerId == mActivePointerId)    {     // This was our active pointer going up. Choose a new     // active pointer and adjust accordingly.     final int newPointerIndex = pointerIndex == 0 ? 1 : 0;     mLastMotionX = ev.getX(newPointerIndex);     mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);     callSuper = true;    }    break;   }   }    if (mOverscrollEffect.isOverscrolling() && !callSuper)   {    return true;   }   else   {    return super.onTouchEvent(ev);   }  }   @Override  protected boolean getChildStaticTransformation(View child, Transformation t)  {   if (child.getWidth() == 0)   {    return false;   }   final int position = child.getLeft() / child.getWidth();   final boolean isFirstOrLast = position == 0 || (position == getAdapter().getCount() - 1);   if (mOverscrollEffect.isOverscrolling() && isFirstOrLast)   {    final float dx = getWidth() / 2;    final int dy = getHeight() / 2;    t.getMatrix().reset();    final float translateX = (float) mOverscrollTranslation      * (mOverscrollEffect.mOverscroll > 0 ? Math.min(mOverscrollEffect.mOverscroll, 1) : Math.max(        mOverscrollEffect.mOverscroll, -1));    mCamera.save();    mCamera.translate(-translateX, 0, 0);    mCamera.getMatrix(t.getMatrix());    mCamera.restore();    t.getMatrix().preTranslate(-dx, -dy);    t.getMatrix().postTranslate(dx, dy);     if (getChildCount() == 1)    {     this.invalidate();    }    else    {     child.invalidate();    }    return true;   }   return false;  } } 

自定义属性如下:

<declare-styleable name="BounceBackViewPager">   <attr name="overscroll_translation_bounce" format="dimension" />    <!-- Duration of animation when user releases the over scroll. Default is 400 ms. -->   <attr name="overscroll_animation_duration_bounce" format="integer" />  </declare-styleable> 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表