androidanimationnineoldandroids

Animations get into infinite loop if they are started in the onAnimationEnd of AnimationListener


Simple task: by pressing button, it scales X value to 0, and when animation finished starts another animation on the second view which scales X from 0 to 1. After 1 second reverse animation should be played and that's all. Running code below I've got infinite animation loop of the first part of animation.

Used nineoldandroids lib, but I don't think this is something really different from native animations framework at least on jelly bean devices.

public class MainActivity extends Activity
{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final View mybutton = findViewById(R.id.mybutton);
    final View myprogress = findViewById(R.id.myprogress);

    mybutton.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            animate(mybutton).scaleX(0).setListener(new AnimatorListenerAdapter()
            {
                @Override
                public void onAnimationEnd(Animator animation)
                {
                    mybutton.setVisibility(View.INVISIBLE);
                    myprogress.setVisibility(View.VISIBLE);
                    ViewHelper.setScaleX(myprogress, 0f);

                    animate(myprogress).scaleX(1).setListener(new AnimatorListenerAdapter()
                    {
                        @SuppressWarnings("ConstantConditions")
                        @Override
                        public void onAnimationEnd(Animator animation)
                        {
                            mybutton.getHandler().postDelayed(new Runnable()
                            {
                                @Override
                                public void run()
                                {
                                    animate(myprogress).scaleX(0).setListener(new AnimatorListenerAdapter()
                                    {
                                        @Override
                                        public void onAnimationEnd(Animator animation)
                                        {
                                            myprogress.setVisibility(View.INVISIBLE);
                                            mybutton.setVisibility(View.VISIBLE);

                                            ViewHelper.setScaleX(mybutton, 0);
                                            animate(mybutton).scaleX(1);
                                        }
                                    });
                                }
                            }, 1000);
                        }
                    });
                }
            });
        }
    });
}

}

Layout is simple:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
>

    <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/mycontainer">


            <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="string text"
                    android:id="@+id/mybutton"
                    />

            <ProgressBar
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignLeft="@id/mybutton"
                    android:layout_alignTop="@id/mybutton"
                    android:layout_alignRight="@id/mybutton"
                    android:layout_alignBottom="@id/mybutton"
                    android:visibility="invisible"
                    android:id="@+id/myprogress"
                    />


            </RelativeLayout>
</RelativeLayout>

Where I was wrong?


Solution

  • Madness! After investigation found source of it: When animate() method called, it creates ViewPropertyAnimator and saves it to the map. When you try to animate this view again with animate() it takes already created viewpropertyanimator from the map and immediately calls onAnimationStart() even before you set new animation parameters, and because animationlistener was set on the first animation, it was triggered! and launched first animation(second part of it). This is creates infinite loop.

    To stop it you MUST clear old listener when you try to animate view second time, so

    animate(mybutton).setListener(null).scaleX(1);
    

    stops infinite loop. Documentation should warn about it, definitely!