Reputation: 14526
I have an ImageView that gets animated when it is added to a layout. When it is removed, I want to reverse the same animation.
Is there a way to reverse an animation in android without recoding it and reversing the parameters?
Upvotes: 52
Views: 45103
Reputation: 1259
Not sure if it's possible to reverse a running animation from its latest point in time without recoding it and keeping track of latest input
parameter passed to whatever interpolator you're using. And that's rather a common case, I searched a lot of places for a simpler solution but couldn't find any easy to implement technique.
Following up on @Ilya Gazman's answer and more importanly on @Trev's comment below it, I had to implement the reverse interpolator as follows:
class ReverseInterpolator @JvmOverloads constructor(
private val delegate: TimeInterpolator = LinearInterpolator()
) : Interpolator {
override fun getInterpolation(input: Float): Float {
return delegate.getInterpolation(1 - input)
}
}
Note that 1 - delegate.getInterpolation
didn't work for me when using AccelerateInterpolator
, but the above version did.
To support reversal of a running animation, I created a wrapper around the interpolator I was interested in:
class StatefulAccelerateInterpolator : AccelerateInterpolator() {
var lastInput: Float = 0f
override fun getInterpolation(input: Float): Float {
lastInput = input
return super.getInterpolation(input)
}
}
After that I'd use ObjectAnimator.setCurrentFraction(1 - lastInput)
when doing the reversal (or the reversal of the reversal which is the initial animation). It's important to do this right before starting the animation, not at an earlier point especially if you have delays and sequential animations.
Of course, the most cumbersome part was reversing the order of animations, because I had multiple in my case, but it is what it is. Depending on the complexity and quantity of animations your case might be not such a big deal. Solution works on API level 22+ which is acceptable in 2025, I think.
Upvotes: 0
Reputation: 7651
No, sadly you cannot do it with the Animation object. But you can simulate it using an interpolator that will inverse the animation:
package com.example.android;
import android.view.animation.Interpolator;
public class ReverseInterpolator implements Interpolator {
@Override
public float getInterpolation(float paramFloat) {
return Math.abs(paramFloat -1f);
}
}
Then on your animation you can set your new interpolator:
myAnimation.setInterpolator(new ReverseInterpolator());
Upvotes: 79
Reputation: 71
Since other answers have already been answered in Java, I will answer in Kotlin
// code inside onViewCreated()
...
var anim: TranslateAnimation = TranslateAnimation(0F,60F,0F, 0F)
anim.duration = 3000
anim.fillAfter = true
anim.repeatCount = -1
anim.repeatMode = Animation.REVERSE // back and forth
binding.asteroid.startAnimation(anim)
...
This is the simplest approach in Kotlin.
Upvotes: 0
Reputation: 12232
You need to use RepeatCount and RepeatMode
kotlin
var anim = TranslateAnimation(0, 100, 0, 100)
anim.repeatCount = Animation.INFINITE // you cant 1,2,...
anim.repeatMode = Animation.REVERSE // you can set REVERSE or RESTART
anim.start()
Upvotes: 1
Reputation: 191
Simplest solution i came up with
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator">
<alpha
android:duration="2000"
android:fromAlpha="0.1"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toAlpha="1.0">
</alpha>
</set>
Upvotes: 6
Reputation: 3922
this worked for me
ObjectAnimator anim = ObjectAnimator.ofFloat(imageViewUpb, "rotation", rotationAngle, rotationAngle + 180);
if (linearLayoutb.getVisibility()==GONE){
linearLayoutb.setVisibility(VISIBLE);
anim.setDuration(500);
anim.start();
rotationAngle += 180;
rotationAngle = rotationAngle%360;
imageViewUpb.animate().rotation(rotationAngle).setDuration(500).start();
}else{
linearLayoutb.setVisibility(GONE);
anim.setDuration(500);
anim.start();
rotationAngle += 180;
rotationAngle = rotationAngle%180;
imageViewUpDownb.animate().rotation(rotationAngle).setDuration(500).start();
}
linearlayoutb is the view that expands when the imageviewUpb faces up
make int rotationAngle = 0; global parameter
Upvotes: 0
Reputation: 261
If you are using animation from xml then an easy way is to made an exact same reverse animation to original animation. Add Animation.AnimationListener
to original animation and in onAnimationEnd
method start the reverse animation.
Upvotes: 0
Reputation: 1546
If you are using Object or ValueAnimator to animate the view, you can simply do
ValueAnimator myAnimator = new ValueAnimator();
myAnimator.reverse()
Documentation can be found here.
Upvotes: 29
Reputation: 32271
Based on pcans idea, you can reverse any interpolator, not just linear.
class ReverseInterpolator implements Interpolator{
private final Interpolator delegate;
public ReverseInterpolator(Interpolator delegate){
this.delegate = delegate;
}
public ReverseInterpolator(){
this(new LinearInterpolator());
}
@Override
public float getInterpolation(float input) {
return 1 - delegate.getInterpolation(input);
}
}
ReverseInterpolator reverseInterpolator = new ReverseInterpolator(new AccelerateInterpolator())
myAnimation.setInterpolator(reverseInterpolator);
Upvotes: 17
Reputation: 18423
I have a similar approach to pcans buts slightly different. It takes an Interpolator
and will effectively pass out values that would be the same as using the passed in Interpolator
normally and then in REVERSE mode. Saves you having to think about the buggy implementations of Animation.REVERSE
across Android. See the code here
public class ReverseInterpolator implements Interpolator {
private final Interpolator mInterpolator;
public ReverseInterpolator(Interpolator interpolator){
mInterpolator = interpolator;
}
@Override
public float getInterpolation(float input) {
return mInterpolator.getInterpolation(reverseInput(input));
}
/**
* Map value so 0-0.5 = 0-1 and 0.5-1 = 1-0
*/
private float reverseInput(float input){
if(input <= 0.5)
return input*2;
else
return Math.abs(input-1)*2;
}
}
Upvotes: 7
Reputation: 4846
You can make the code remember the original position and the end position. And let your code dynamically get those values when triggering animation.
Upvotes: 1