2012-12-07 72 views
2

我谷歌的問題很多天。找到一個解決方案,即在Viewpager的第一個和結束處添加一個空白的View int。當viewpager.getcurrentItem()== 0,然後重新指向1. 在我看來這種方法很差 因爲這個解決方案改變了viewpager.getcount()。可能會造成很多問題。android VIewpager如何實現約束效果

我發現一個類來hanler此, http://developer.android.com/reference/android/support/v4/widget/EdgeEffectCompat.html 但我不知道如何使用它! 有人可以給我一個例子嗎?

https://docs.google.com/open?id=0B9poNqa96rJyLU5Va0lnMHJGY0k

當我鬆開手指回到原來的位置

+0

界的影響?或反彈效應,你在尋找什麼樣的彈跳,請詳細說明 – Shakti

+0

該Pic已上傳到谷歌文檔。你可以在問題 – Tony

+0

中找到它或者像這個問題http://stackoverflow.com/questions/10773565/visual-indication-of-over-scroll-in-android – Tony

回答

11

如果現在還有人會想知道如何做到這一點我居然發現renard314基於一個輝煌的圖書館的解決方案:https://github.com/inovex/ViewPager3D 所有信貸應該去他:) 不管怎麼說,擴展ViewPager:

public class BounceBackViewPager extends ViewPager { 

/** 
* maximum z distance to translate child view 
*/ 
final static int DEFAULT_OVERSCROLL_TRANSLATION = 150; 

/** 
* 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, DEFAULT_OVERSCROLL_TRANSLATION); 
    mOverscrollAnimationDuration = a.getInt(R.styleable.BounceBackViewPager_overscroll_animation_duration, 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; 
} 

}

,並在文件夾中的某個位置添加一個資源文件:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
<declare-styleable name="BounceBackViewPager"> 
    <!-- 
    determines the maximum amount of translation along the z-axis during the overscroll. 
    Default is 150. 
    --> 
    <attr name="overscroll_translation" format="dimension" /> 

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

我所要做的就是改變改造,支持平移X。我強烈建議先閱讀ViewPager3D的文檔。

+1

要添加一個偉大的答案從Piotr頂部,如果有人會有我一樣的問題(動畫只能從Api 11)將這種行爲添加到11以下的Api中,您可以使用另一個偉大的庫 - 「NineOldAndroids」,它將所有此api導出爲在Honeycomb中啓動的動畫,可以在具有min Api 1的應用程序中使用該動畫。 從這裏得到了答案: http://stackoverflow.com/questions/10267291/animation-before-api-11 – Lucas

+0

很好的答案。這隻適用於在第一個元素上滾動的情況,而不會影響最後一個滾動。我錯過了什麼嗎? –

+0

我應該說我每頁顯示多個視圖,覆蓋我的適配器中的getPageWidth()來這樣做。 –

0

BounceBackViewPager有一個錯誤,當在一個頁面(屏幕)中顯示多個頁面(片段)時,只有第一頁有OverscrollEffect,也就是說,您可以在屏幕上看到多個頁面,只有第一頁會反彈回來,它看起來很奇怪。只有左側有超壓效應,右側沒有。

我的工具是:

@Override 
public float getPageWidth(int position) { 
      return 1/mShowPictureSize; 
}