Monday, October 1, 2018

How to achieve Buttery Smooth ViewPager Shake animation

Leave a Comment

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 Answers

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment