package agency.tango.materialintroscreen.widgets; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; import android.widget.RelativeLayout; import agency.tango.materialintroscreen.R; import agency.tango.materialintroscreen.listeners.IFinishListener; public class OverScrollViewPager extends RelativeLayout { private SwipeableViewPager swipeableViewPager = null; private boolean mIsBeingDragged = false; private float mMotionBeginX = 0; private float positionOffset = 0; private int mTouchSlop; private IFinishListener finishListener; public OverScrollViewPager(Context context) { this(context, null); } public OverScrollViewPager(Context context, AttributeSet attrs) { this(context, attrs, 0); } public OverScrollViewPager(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); swipeableViewPager = createOverScrollView(); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); addView(swipeableViewPager, layoutParams); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { mMotionBeginX = event.getX(); mIsBeingDragged = false; } else if (action == MotionEvent.ACTION_MOVE) { if (!mIsBeingDragged) { float scrollDirectionDiff = event.getX() - mMotionBeginX; if (Math.abs(scrollDirectionDiff) > mTouchSlop) { if (canOverScrollAtEnd() && scrollDirectionDiff < 0f) { mIsBeingDragged = true; } } } } return mIsBeingDragged; } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); float moveOffset = event.getX() - mMotionBeginX; if (action == MotionEvent.ACTION_MOVE) { moveOverScrollView(moveOffset); } else if (action == MotionEvent.ACTION_UP) { if (positionOffset > 0.5f) { finishOverScrollViewWithAnimation(moveOffset); } else { resetOverScrollViewWithAnimation(moveOffset); } mIsBeingDragged = false; } return true; } public SwipeableViewPager getOverScrollView() { return swipeableViewPager; } public void registerFinishListener(IFinishListener listener) { finishListener = listener; } private void moveOverScrollView(float currentX) { if (canScroll(currentX)) { scrollTo((int) -currentX, 0); positionOffset = calculateOffset(); swipeableViewPager.onPageScrolled(swipeableViewPager.getAdapter().getLastItemPosition(), positionOffset, 0); if (shouldFinish()) { finishListener.doOnFinish(); } } } private float calculateOffset() { return ((100f * getScrollX()) / getWidth()) / 100f; } private boolean shouldFinish() { return positionOffset == 1f; } private boolean canScroll(float currentX) { return currentX <= 0f; } private void resetOverScrollViewWithAnimation(final float currentX) { post(new SmoothScrollRunnable((int) currentX, 0, 300, new AccelerateInterpolator())); } private void finishOverScrollViewWithAnimation(float currentX) { post(new SmoothScrollRunnable((int) currentX, -getWidth(), 300, new AccelerateInterpolator())); } private boolean canOverScrollAtEnd() { SwipeableViewPager viewPager = getOverScrollView(); PagerAdapter adapter = viewPager.getAdapter(); if (null != adapter && adapter.getCount() > 0) { if (viewPager.alphaExitTransitionEnabled() && viewPager.getCurrentItem() == adapter.getCount() - 1) { return true; } return false; } return false; } private SwipeableViewPager createOverScrollView() { SwipeableViewPager swipeableViewPager = new SwipeableViewPager(getContext(), null); swipeableViewPager.setId(R.id.swipeable_view_pager); return swipeableViewPager; } final class SmoothScrollRunnable implements Runnable { private final Interpolator interpolator; private final int scrollToPosition; private final int scrollFromPosition; private final long duration; private long startTime = -1; private int currentPosition = -1; SmoothScrollRunnable(int fromPosition, int toPosition, long duration, Interpolator scrollAnimationInterpolator) { scrollFromPosition = fromPosition; scrollToPosition = toPosition; interpolator = scrollAnimationInterpolator; this.duration = duration; } @Override public void run() { if (startTime == -1) { startTime = System.currentTimeMillis(); } else { long normalizedTime = (1000 * (System.currentTimeMillis() - startTime)) / duration; normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0); final int deltaY = Math.round((scrollFromPosition - scrollToPosition) * interpolator.getInterpolation(normalizedTime / 1000f)); currentPosition = scrollFromPosition - deltaY; moveOverScrollView(currentPosition); } if (scrollToPosition != currentPosition) { ViewCompat.postOnAnimation(OverScrollViewPager.this, this); } } } }