android VIewpager如何实现绑定效果

时间:2012-12-07 09:00:59

标签: android-viewpager bounce

我多天来谷歌这个问题。找到一个解决方案,在Viewpager的第一个和结尾处添加一个空白视图。当viewpager.getcurrentItem()== 0时,则重新指向1。 在我看来,这种方法非常差。 因为,此解决方案更改了viewpager.getcount()。可能会造成很多问题。

我找到了一个汉德这个班, http://developer.android.com/reference/android/support/v4/widget/EdgeEffectCompat.html 但我不知道如何使用它! 有人可以举个例子吗?

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

当我将手指放回原位时

2 个答案:

答案 0 :(得分: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;
}

}

并在 values 文件夹中的某处添加资源文件:

<?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>

我所要做的就是改变转换以支持translateX。我强烈建议您首先阅读ViewPager3D的文档。

答案 1 :(得分:0)

BounceBackViewPager有一个错误,当在一个页面(屏幕)中显示多个页面(片段)时,只有第一个页面具有OverscrollEffect,也就是说,您可以在屏幕上看到多个页面,只有第一个页面会反弹,看起来很奇怪。并且只有左侧有过度效应,右侧没有。

我的工具是:

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