Search code examples
androidandroid-animationtranslate-animation

How to achieve uniform velocity throughout a Translate Animation?


I have an infinite translate animation applied to an ImageView:

Animation animation = new TranslateAnimation(0, 0, -500, 500);
animation.setDuration(4000);
animation.setFillAfter(false);
myimage.startAnimation(animation);
animation.setRepeatCount(Animation.INFINITE);

What I have noticed is that the translation process is slower when the image is near the starting and ending point compared to when its near half the distance (middle point).

I guess the velocity for translate animations on android is not uniform.

How do I make the velocity uniform throughout the process ?


Solution

  • I did some source-diving to investigate this. First, note that if a linear interpolator is used to provide interpolatedTime values to the applyTransformation method of TranslateAnimation, the resulting translation will have constant velocity (because the offsets dx and dy are linear functions of interpolatedTime (lines 149-160)):

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float dx = mFromXDelta;
        float dy = mFromYDelta;
        if (mFromXDelta != mToXDelta) {
            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
        }
        if (mFromYDelta != mToYDelta) {
            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
        }
        t.getMatrix().setTranslate(dx, dy);
    }
    

    applyTransformation is called by the getTransformation method of the base Animation class (lines 869-870):

    ...
    final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
    applyTransformation(interpolatedTime, outTransformation);
    ...
    

    According to the documentation for the setInterpolator method (lines 382-392), mInterpolator should default to a linear interpolator:

    /**
     * Sets the acceleration curve for this animation. Defaults to a linear
     * interpolation.
     *
     * @param i The interpolator which defines the acceleration curve
     * @attr ref android.R.styleable#Animation_interpolator
     */
    public void setInterpolator(Interpolator i) {
        mInterpolator = i;
    }
    

    However, this seems to be false: both constructors in the Animation class call the ensureInterpolator method (lines 803-811):

    /**
     * Gurantees that this animation has an interpolator. Will use
     * a AccelerateDecelerateInterpolator is nothing else was specified.
     */
    protected void ensureInterpolator() {
        if (mInterpolator == null) {
            mInterpolator = new AccelerateDecelerateInterpolator();
        }
    }
    

    which suggests that the default interpolator is an AccelerateDecelerateInterpolator. This explains the behavior you describe in your question.

    To actually answer your question, then, it would appear that you should amend your code as follows:

    Animation animation = new TranslateAnimation(0, 0, -500, 500);
    animation.setInterpolator(new LinearInterpolator());
    animation.setDuration(4000);
    animation.setFillAfter(false);
    myimage.startAnimation(animation);
    animation.setRepeatCount(Animation.INFINITE);