Search code examples
androidanimationandroid-animationtranslate-animationslowdown

How to slow down an animation in its middle while being executed?


As the question is clearly stated, I want to slow down a TranslateAnimation while it is being executed when a user clicks on a certain button.

This is how I instantiate the TranslateAnim where -textViewHeight and layoutHeight are just some values I already instantiated in earlier time:

It goes from up to bottom.

TranslateAnim translateAnim = new TranslateAnim(0, 0, -textViewHeight, layoutHeight);
translateAnim.setDuration(20000);
translateAnim.setInterpolator(new LinearInterpolator());
textView.startAnimation(translateAnim);

This is the method where I want to slow the TranslateAnim down:

It didn't work as I expected though.

public void slowDown() {
    translateAnim.setStartTime(translateAnim.getStartTime() - 20000);
    translateAnim.setDuration((20000 - translateAnim.getElapsedTime()) * 2);
}

I also tried doing that but still no luck:

translateAnim.setInterpolator(new DecelerateInterpolator());

My custom TranslateAnim class extending TranslateAnimation:

public class TranslateAnim extends TranslateAnimation {

    private long mElapsedAtPause, elapsedTime;
    private boolean mPaused = false;

    public TranslateAnim(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
        super(fromXDelta, toXDelta, fromYDelta, toYDelta);
    }

    @Override
    public boolean getTransformation(long currentTime, Transformation outTransformation) {
        if (mPaused && mElapsedAtPause == 0) {
            mElapsedAtPause = currentTime - getStartTime();
        }
        if (mPaused) {
            setStartTime(currentTime - mElapsedAtPause);
        }
        elapsedTime = currentTime - getStartTime();
        return super.getTransformation(currentTime, outTransformation);
    }

    public long getElapsedTime() {
        return elapsedTime;
    }

    public void slowDown() {
        translateAnim.setStartTime(translateAnim.getStartTime() - 20000);
        translateAnim.setDuration((20000 - translateAnim.getElapsedTime()) * 2);
    }

    public void pause() {
        mElapsedAtPause = 0;
        mPaused = true;
    }

    public void resume() {
        mPaused = false;
    }

    @Override
    public void cancel() {
        super.cancel();
        elapsedTime = 0;
        mElapsedAtPause = 0;
        mPaused = false;
    }
}

Is there any workaround for this?


Solution

  • Use a custom Interpolator instead of a linear one. A linear one causes it to have equal time slices. A custom one can have the time slices in the middle be longer.

    For example:

    public class CustomInterpolator implements Interpolator {
    
        public boolean slowMode;
        float lastInput;
        float lastInputBeforeSlowed;
    
        @Override
        public float getInterpolation(float input) {
            if (!slowMode) {
                //Should be edited
                lastInput = input;
                return input;
            } else {
                return (input - lastInputBeforeSlowed) * .5f + lastInputBeforeSlowed;
            }
        }
    
        public void enterSlowMode() {
            slowMode = true;
            lastInputBeforeSlowed = lastInput;
        }
    
        public void endSlowMode() {
            slowMode = false;
            //Should be edited
        }
    }