Android: Animation Position Resets After Complete

Android

Android Problem Overview


I'm using an xml defined animation to slide a view off the screen. The problem is, as soon as the animation completes it resets to its original position. I need to know how to fix this. Here's the xml:

<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
   <translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="500"/></set>

Here's the Java that I use to call it:

    homeScrn = (View)findViewById(R.id.homescreen);
    slideLeftOut = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
    
    //Set Click Listeners For Menu
    btnHelp.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            LayoutInflater.from(getApplicationContext()).inflate(R.layout.help, (ViewGroup)findViewById(R.id.subpage), true);
            homeScrn.startAnimation(slideLeftOut);
        }
    });

So basically what happens is I inflate a view underneath one. Then I animate the view on top off to the left. As soon as it gets off screen and the animation is finished it resets its position back.

Android Solutions


Solution 1 - Android

Finally got a way to work around,the right way to do this is setFillAfter(true),

if you want to define your animation in xml then you should do some thing like this

<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/decelerate_interpolator"
     android:fillAfter="true">
    
    <translate 
        android:fromXDelta="0%"
        android:toXDelta="-100%"
        android:duration="1000"/>
   
</set>

you can see that i have defined filterAfter="true" in the set tag,if you try to define it in translate tag it won't work,might be a bug in the framework!!

and then in the Code

Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_out);
someView.startAnimation(anim);

OR

TranslateAnimation animation = new TranslateAnimation(-90, 150, 0, 0);
    			
animation.setFillAfter(true);
    			
animation.setDuration(1800);
    			
someView.startAnimation(animation);

then it will surely work!!

Now this is a bit tricky it seems like the view is actually move to the new position but actually the pixels of the view are moved,i.e your view is actually at its initial position but not visible,you can test it if have you some button or clickable view in your view(in my case in layout),to fix that you have to manually move your view/layout to the new position

public TranslateAnimation (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

new TranslateAnimation(-90, 150, 0, 0);

now as we can see that our animation will starts from -90 x-axis to 150 x-axis

so what we do is set

someView.setAnimationListener(this);

and in

public void onAnimationEnd(Animation animation)
{
   someView.layout(150, 0, someView.getWidth() + 150, someView.getHeight());
}

now let me explain public void layout (int left, int top, int right, int botton)

it moves your layout to new position first argument define the left,which we is 150,because translate animation has animated our view to 150 x-axis, top is 0 because we haven't animated y-axis,now in right we have done someView.getWidth() + 150 basically we get the width of our view and added 150 because we our left is now move to 150 x-axis to make the view width to its originall one, and bottom is equals to the height of our view.

I hope you people now understood the concept of translating, and still you have any questions you can ask right away in comment section,i feel pleasure to help :)

EDIT Don't use layout() method as it can be called by the framework when ever view is invalidated and your changes won't presist, use LayoutParams to set your layout parameters according to your requirement

Solution 2 - Android

The animations are working as expected. Just because you animated something does not mean you actually moved it. An animation only affects drawn pixels during the animation itself, not the configuration of the widgets.

You need to add an AnimationListener, and in the onAnimationEnd() method, do something that makes your move permanent (e.g., removes the view on top from its parent, marks the view on top as having visibility GONE).

Solution 3 - Android

I might be a bit late in replying, but I have the solution for this:

Just add android:fillAfter="true" in your xml

Solution 4 - Android

You can use

fillAfter=true
fillEnabled=true

your View will'not reset to original position, but if you have some buttons or something else in your view , their position will not change.

You must use ObjectAnimator , it works from API 11 level . It changes View position automatic,

here is the example

ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(mContent_container, "translationX", startX, endX);
objectAnimator.setDuration(1000);
objectAnimator.start();

Thanks JUL for his answer

If your app not found object animator, change the API level from Project -> properties -> Android , than import android.animation.ObjectAnimator;

Regards Hayk

Solution 5 - Android

You need to add this command:

final Animation animSlideLeft = new TranslateAnimation(0, -80, 0,-350, 0, 0, 0, 0);
animSlideLeft.setFillAfter(true);

Solution 6 - Android

I went to the same question and solved it with setting the marginLeft at OnAnimationEnd()..

MarginLayoutParams params = (MarginLayoutParams) this.getLayoutParams(); params.setMargins(this.getWidth()*position, 0,0,0); this.setLayoutParams(params);

Solution 7 - Android

This problem has bothered for more than a week. And Muhammad's answer pointed me a strightforward way to solve it.

I used sever layout to implement side menus with animation. "view.layout will not be persistent. You should use LayoutParams, which will be persistent."

Here's my solution: (Use what kind of LayoutParameter is depended on your own parent's layout):

@Override
public void onAnimationEnd(Animation animation) {
FrameLayout.LayoutParams layoutParams=new FrameLayout.LayoutParams(screenWidth, screenHeight);
layoutParams.setMargins((int) -(screenWidth * RATE), 0,
            (int) (screenWidth - screenWidth * RATE), screenHeight);
    for(View v:views) {

        v.setLayoutParams(layoutParams);
        //v.layout((int) -(screenWidth * RATE), 0, (int) (screenWidth - screenWidth * RATE), screenHeight);
        v.clearAnimation();
    }
}

Solution 8 - Android

fillAfter = true will do the job to transform view to new animation positon.

rotateAnimation.fillAfter = true;

If anybody interested to move arrow_up & arrow_down animations for a recycle view expands/collapse behavior.

  1. Initial state: Collapse
  2. call the method with a child visibility flag(View.VISIBLE/View.GONE).

Below is code belongs to animations.

fun setArrowImage(imageView: ImageView, childVisibility: Int) {

    val angleRange = if (childVisibility == View.VISIBLE) Pair(0F, 180F) else 
    Pair(100f, 0F)

    val rotateAnimation = RotateAnimation(
        angleRange.first, angleRange.second,
        Animation.RELATIVE_TO_SELF, 0.5f,
        Animation.RELATIVE_TO_SELF, 0.5f
    )
    rotateAnimation.duration = 300
    rotateAnimation.fillAfter = true;

    imageView.startAnimation(rotateAnimation)
}

Solution 9 - Android

Use helper methods below to easily animate your view. Then you can animate and reset after completion like this:

// Animate the view:

fly(
  view1,
  (float) 0,
  (float) 0,
  (float) 0,
  (float) 1000,
  250,
  null,
  null,
  () -> {

    // Return the view to initial position on animation end:  

    fly(
      view1,
      (float) 0,
      (float) 0,
      (float) 0,
      (float) 0,
      0);

    return null;
  });

Helper methods:

/**
 * Translation of a view.
 */
public static void fly(
  View view,
  float fromXDelta,
  float toXDelta,
  float fromYDelta,
  float toYDelta,
  int duration) {

  fly(
    view,
    fromXDelta,
    toXDelta,
    fromYDelta,
    toYDelta,
    duration,
    null,
    null,
    null);
}

/**
 * Translation of a view with handlers.
 *
 * @param view       A view to animate.
 * @param fromXDelta Amount to shift by X axis for start position.
 * @param toXDelta   Amount to shift by X axis for end position.
 * @param fromYDelta Amount to shift by Y axis for start position.
 * @param toYDelta   Amount to shift by Y axis for end position.
 * @param duration   Animation duration.
 * @param start      On animation start. Otherwise NULL.
 * @param repeat     On animation repeat. Otherwise NULL.
 * @param end        On animation end. Otherwise NULL.
 */
public static void fly(
  View view,
  float fromXDelta,
  float toXDelta,
  float fromYDelta,
  float toYDelta,
  int duration,
  Callable start,
  Callable repeat,
  Callable end) {

  Animation animation;

  animation =
    new TranslateAnimation(
      fromXDelta,
      toXDelta,
      fromYDelta,
      toYDelta);

  animation.setDuration(
    duration);

  animation.setInterpolator(
    new DecelerateInterpolator());

  animation.setFillAfter(true);

  view.startAnimation(
    animation);

  animation.setAnimationListener(new AnimationListener() {

    @Override
    public void onAnimationStart(Animation animation) {

      if (start != null) {
        try {

          start.call();

        } catch (Exception e) {
          e.printStackTrace();
        }
      }

    }

    @Override
    public void onAnimationEnd(Animation animation) {

      if (end != null) {
        try {

          end.call();

        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }

    @Override
    public void onAnimationRepeat(
      Animation animation) {

      if (repeat != null) {
        try {

          repeat.call();

        } catch (Exception e) {
          e.printStackTrace();
        }
      }  
    }
  });
}

Solution 10 - Android

Nothing from answers worked with me; although @Muhammad Babar answer inspired me to get a solution:

He mentioned that we can layout the view to the new position upon animation end:

public void onAnimationEnd(Animation animation) {
   someView.layout(150, 0, someView.getWidth() + 150, someView.getHeight());
}

Although this didn't work with me instead; I had to do that in a Handler with delayed Runnable using a delay value that is slightly less than the animation duration.

So, in my case the animation duration is 500 msec, and I used a handler on 450 mesec, so it gets triggered just before the animation ends by 50 msec.

Handler().postDelayed({
	someView.layout(150, 0, someView.width + 150, someView.hight)
	someView.requestLayout()
	someView.forceLayout()
}, 450)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLoneWolfPRView Question on Stackoverflow
Solution 1 - AndroidMuhammad BabarView Answer on Stackoverflow
Solution 2 - AndroidCommonsWareView Answer on Stackoverflow
Solution 3 - AndroidsreeramView Answer on Stackoverflow
Solution 4 - AndroidHayk NahapetyanView Answer on Stackoverflow
Solution 5 - AndroidWattanapong SuttapakView Answer on Stackoverflow
Solution 6 - AndroidLukas OlsenView Answer on Stackoverflow
Solution 7 - AndroidRanger WayView Answer on Stackoverflow
Solution 8 - AndroidMuhammad MaqsoodView Answer on Stackoverflow
Solution 9 - AndroidZonView Answer on Stackoverflow
Solution 10 - AndroidZainView Answer on Stackoverflow