在我的应用程序中,我试图实现过度滚动的反弹效果,而不是机器人无聊的正常过度滚动技术。
我正在尝试将此过卷效果应用于处理相当多片段的FragmentPagerAdapter。我该如何实现呢?
如果有帮助,这是我当前的FragmentPagerAdapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private MainActivity mainActivity;
public SectionsPagerAdapter(MainActivity mainActivity, FragmentManager fm) {
super(fm);
this.mainActivity = mainActivity;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FragmentPages.FragmentPage1();
case 1:
return new FragmentPages.FragmentPage2();
case 2:
return new FragmentPages.FragmentPage3();
case 3:
return new FragmentPages.FragmentPage4();
case 4:
return new FragmentPages.FragmentPage5();
case 5:
return new FragmentPages.FragmentPage6();
case 6:
return new FragmentPages.FragmentPage7();
case 7:
return new FragmentPages.FragmentPage8();
case 8:
return new FragmentPages.FragmentPage9();
case 9:
return new FragmentPages.FragmentPage10();
case 10:
return new FragmentPages.FragmentPage11();
}
return null;
}
@Override
public int getCount() {
return 11;
}
}
答案 0 :(得分:0)
这里是我项目的样本。大部分代码来自answer
public class BounceViewPager extends ViewPager {
private final static int LONG_ANIM_DURATION = 850;
private final static int SHORT_ANIM_DURATION = 150;
private final static int DEFAULT_OVERSCROLL_ANIMATION_DURATION = 200;
private GestureDetector xScrollDetector;
private FixedSpeedScroller scroller;
class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceX) > Math.abs(distanceY);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN: {
lastMotionX = ev.getX();
activePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
lastMotionX = MotionEventCompat.getX(ev, index);
activePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
}
if (xScrollDetector.onTouchEvent(ev)) {
super.onInterceptTouchEvent(ev);
return true;
}
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
Timber.e(e, "");
return false;
}
}
private final static int INVALID_POINTER_ID = -1;
private class OverScrollEffect {
private float overScroll;
private Animator animator;
public void setPull(final float deltaDistance) {
overScroll = deltaDistance;
invalidateVisibleChildren();
}
private void onRelease() {
if (animator != null && animator.isRunning()) {
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
startAnimation();
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
animator.cancel();
} else {
startAnimation();
}
}
private void startAnimation() {
animator = ObjectAnimator.ofFloat(this, "pull", overScroll, 0);
animator.setInterpolator(new DecelerateInterpolator());
final float scale = Math.abs(-overScroll);
animator.setDuration((long) (overScrollAnimationDuration * scale));
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
private boolean isOverScrolling() {
if (scrollPosition == 0 && overScroll < 0) {
return true;
}
final boolean isLast = (getAdapter().getCount() - 1) == getCurrentItem();
return isLast && overScroll > 0;
}
}
final private OverScrollEffect overscrollEffect = new OverScrollEffect();
final private Camera camera = new Camera();
private float lastMotionX;
private int activePointerId;
private int scrollPosition;
private float scrollPositionOffset;
final private int touchSlop;
private float overScrollTranslation;
private int overScrollAnimationDuration;
public BounceViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
xScrollDetector = new GestureDetector(getContext(), new XScrollDetector());
setStaticTransformationsEnabled(true);
final ViewConfiguration configuration = ViewConfiguration.get(context);
touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
super.setOnPageChangeListener(new MyOnPageChangeListener());
init();
}
private void init() {
overScrollTranslation = UiUtil.getDisplayWidth((Activity) getContext()) / 3;
overScrollAnimationDuration = DEFAULT_OVERSCROLL_ANIMATION_DURATION;
try {
Field mScroller;
mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
scroller = new FixedSpeedScroller(getContext());
setShortAnimDuration();
mScroller.set(this, scroller);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
Timber.e(e, "");
}
}
private void invalidateVisibleChildren() {
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).invalidate();
}
}
private class MyOnPageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
scrollPosition = position;
scrollPositionOffset = positionOffset;
invalidateVisibleChildren();
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(final int state) {
if (state == SCROLL_STATE_IDLE) {
scrollPositionOffset = 0;
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean callSuper = false;
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
callSuper = true;
lastMotionX = ev.getX();
activePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEventCompat.ACTION_POINTER_DOWN: {
callSuper = true;
final int index = MotionEventCompat.getActionIndex(ev);
lastMotionX = MotionEventCompat.getX(ev, index);
activePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEvent.ACTION_MOVE: {
if (activePointerId != INVALID_POINTER_ID) {
final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final float deltaX = lastMotionX - x;
final float oldScrollX = getScrollX();
final int width = getWidth();
final int widthWithMargin = width + getPageMargin() - (getPaddingLeft() + getPaddingRight());
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 (scrollPositionOffset == 0) {
if (scrollX < leftBound) {
if (leftBound == 0) {
final float over = deltaX + touchSlop;
overscrollEffect.setPull(over / width);
}
} else if (scrollX > rightBound) {
if (rightBound == lastItemIndex * widthWithMargin) {
final float over = scrollX - rightBound - touchSlop;
overscrollEffect.setPull(over / width);
}
}
} else {
lastMotionX = x;
}
} else {
overscrollEffect.onRelease();
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
callSuper = true;
activePointerId = INVALID_POINTER_ID;
overscrollEffect.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 == activePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
lastMotionX = ev.getX(newPointerIndex);
activePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
callSuper = true;
}
break;
}
}
return overscrollEffect.isOverScrolling() && !callSuper || 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 (overscrollEffect.isOverScrolling() && isFirstOrLast) {
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
final float dx = getWidth() / 2;
final int dy = getHeight() / 2;
t.getMatrix().reset();
final float translateX = overScrollTranslation * (overscrollEffect.overScroll > 0 ?
Math.min(overscrollEffect.overScroll, 1) : Math.max(overscrollEffect.overScroll, -1));
camera.save();
camera.translate(-translateX, 0, 0);
camera.getMatrix(t.getMatrix());
t.getMatrix().preTranslate(-dx, -dy);
t.getMatrix().postTranslate(dx, dy);
camera.restore();
this.invalidate();
return true;
}
return false;
}
private void setAnimationDuration(int duration) {
scroller.setDuration(duration);
}
public void setLongAnimDuration() {
setAnimationDuration(LONG_ANIM_DURATION);
}
public void setShortAnimDuration() {
setAnimationDuration(SHORT_ANIM_DURATION);
}
}
public class FixedSpeedScroller extends Scroller {
private int duration = 150;
public FixedSpeedScroller(Context context) {
super(context, new LinearOutSlowInInterpolator());
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, this.duration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, duration);
}
public void setDuration(int duration) {
this.duration = duration;
}
}