I am investigating Android ViewPager animation in my current project.
I would like to give my users an affordance in relation to my ViewPager.
The effect I am looking for it to "shake" the ViewPager from left to right to partially show the neighbouring pages to the selected page.
When the user selects the first page (F), I would "Shake" between F and F + 1.
When the user selects the last page (L), I would "Shake" between L and L - 1.
Otherwise, when the user selects any other page (X), I would "Shake" between X - 1, X and X + 1.
I have tried the following approaches which have all given unsatisfactory results.
Fake drag using screen density to calculate shake distance and valueAnimator and ObjectAnimator. Animating ViewPager.PageTransformer.
I am unable to get a satisfactory "Shake" effect.
I would like to achieve a Spring Dampening style of animation.
The FakeDrag was closest, although the effect was not reliable, it seemed to never work correctly the first time I started the animation.
Even the FakeDrag would not give the desired smooth shake between page swipes.
I would like to employ the physics-based animations on my view pager but couldn't see how.
Is it possible to achieve my desired animation?
UPDATE
I have developed this code which gives the desired effect, however its JANKY!
@Override protected void onResume() { super.onResume(); final Handler handler = new Handler(); handler.postDelayed(() -> animateViewPager(ANIMATION_OFFSET, ANIMATION_DURATION), ANIMATION_DELAY); } /** * @param offset * @param duration */ private void animateViewPager(final int offset, final int duration) { if (animator.isRunning()) { return; } animator.removeAllUpdateListeners(); animator.removeAllListeners(); animator.setIntValues(0, -offset); animator.setDuration(duration); animator.setRepeatCount(getRepeatCount()); animator.setRepeatMode(ValueAnimator.RESTART); animator.addUpdateListener(constructUpdateListener()); animator.addListener(constructAnimatorListener()); animator.start(); } /** * * @return */ private Animator.AnimatorListener constructAnimatorListener() { return new AnimatorListenerAdapter() { @Override public void onAnimationStart(final Animator animation) { animFactor = 1; } @Override public void onAnimationEnd(final Animator animation) { viewPager.endFakeDrag(); if (xAnimation == null) { xAnimation = createSpringAnimation(viewPager, SpringAnimation.X, 0, SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_HIGH_BOUNCY); } else { xAnimation.cancel(); } viewPager.animate().x(viewPager.getWidth() * 0.1f).setDuration(0).start(); xAnimation.start(); } @Override public void onAnimationRepeat(final Animator animation) { animFactor = -1; } }; } /** * @return */ private int getRepeatCount() { if (isOnlyPage()) { return 0; } if (isFirstListItem()) { return REPEAT_ONCE; } if (isLastListItem()) { return REPEAT_ONCE; } return REPEAT_TWICE; } @SuppressWarnings("StatementWithEmptyBody") private ValueAnimator.AnimatorUpdateListener constructUpdateListener() { return animation -> { final Integer value = animFactor * (Integer) animation.getAnimatedValue(); if (viewPager.isFakeDragging()) { } else { viewPager.beginFakeDrag(); } viewPager.fakeDragBy(value); }; } /** * @param view * @param property * @param finalPosition * @param stiffness * @param dampingRatio * @return */ private SpringAnimation createSpringAnimation(final View view, final DynamicAnimation.ViewProperty property, final float finalPosition, final float stiffness, final float dampingRatio) { final SpringAnimation animation = new SpringAnimation(view, property); final SpringForce springForce = new SpringForce(finalPosition); springForce.setStiffness(stiffness); springForce.setDampingRatio(dampingRatio); animation.setSpring(springForce); return animation; }
0 comments:
Post a Comment