Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > android自定义ViewPager水平滑动弹性

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

作者:qndroid

这篇文章主要为大家详细介绍了android自定义ViewPager水平滑动弹性,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

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

代码如下:

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> 

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

您可能感兴趣的文章:
阅读全文