Tuesday, 25 September 2018

How to achieve Buttery Smooth ViewPager Shake animation

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;
    }



from How to achieve Buttery Smooth ViewPager Shake animation

No comments:

Post a Comment