androidandroid-animationandroid-appcompatviewpropertyanimator

StackOverflowError when using ViewCompat


I'm using viewCompat to compatible my animation to lower api (10).

But when I deploy this on emulator I get StackOverflowError

This is my code:

private void fabFadeIn(){

    if (floatingActionButton.getVisibility() == View.GONE) {

        floatingActionButton.setVisibility(View.VISIBLE);

        ViewCompat.setAlpha(floatingActionButton, 0f);
        ViewCompat.setScaleX(floatingActionButton, 0f);
        ViewCompat.setScaleY(floatingActionButton, 0f);

        ViewCompat.animate(floatingActionButton)
                .alpha(1)
                .scaleX(1)
                .scaleY(1)
                .setDuration(300)
                .setInterpolator(new OvershootInterpolator())
                .setListener(new ViewPropertyAnimatorListener() {
                    @Override
                    public void onAnimationStart(View view) {

                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        ViewCompat.animate(floatingActionButton).setInterpolator(new LinearOutSlowInInterpolator()).start();
                    }

                    @Override
                    public void onAnimationCancel(View view) {

                    }
                })
                .start();


    }

}

And this is error:

 java.lang.StackOverflowError
                                                                       at java.lang.Thread.currentThread(Thread.java:557)
                                                                       at java.lang.ThreadLocal.get(ThreadLocal.java:59)
                                                                       at android.view.ViewRoot.getRunQueue(ViewRoot.java:3340)
                                                                       at android.view.View.removeCallbacks(View.java:5407)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.removeStartMessage(ViewPropertyAnimatorCompat.java:341)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:268)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.startAnimation(ViewPropertyAnimatorCompat.java:308)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimatorCompatImpl.start(ViewPropertyAnimatorCompat.java:269)
                                                                       at android.support.v4.view.ViewPropertyAnimatorCompat.start(ViewPropertyAnimatorCompat.java:1249)
                                                                       at com.test.app.activities.MainActivity$2.onAnimationEnd(MainActivity.java:305)
                                                                    at android.support.v4.view.ViewPropertyAnimatorCompat$BaseViewPropertyAnimat

As you can see error goes back to onAnimationEnd section of code.


Solution

  • If we go to the sources, we will see, that one of realisations of compatible animate method is:

    @Override
    public ViewPropertyAnimatorCompat animate(View view) {
        if (mViewPropertyAnimatorCompatMap == null) {
            mViewPropertyAnimatorCompatMap = new WeakHashMap<>();
        }
        ViewPropertyAnimatorCompat vpa = mViewPropertyAnimatorCompatMap.get(view);
        if (vpa == null) {
            vpa = new ViewPropertyAnimatorCompat(view);
            mViewPropertyAnimatorCompatMap.put(view, vpa);
        }
        return vpa;
    }
    

    So in internal compatible animations use cache of Animators attached to your view. Now let's return to your case. You have view floatingActionButton. And you call ViewCompat.animate() for it. Then in internal implementations ViewPropertyAnimatorCompat is created and assigned to your view. Then you set'ing listener for it.

    After animation is finished you call animate again and instead of creating new ViewPropertyAnimatorCompat (it seems logicaly) you received previously created ViewPropertyAnimatorCompat with your assigned listener. And thus you have endless loop.

    To fix it, you need to write something like:

        ViewCompat.animate(floatingActionButton)
                .alpha(1)
                .scaleX(1)
                .scaleY(1)
                .setDuration(300)
                .setInterpolator(new OvershootInterpolator())
                .setListener(new ViewPropertyAnimatorListener() {
                    @Override
                    public void onAnimationStart(View view) {
    
                    }
    
                    @Override
                    public void onAnimationEnd(View view) {
                        ViewCompat.animate(floatingActionButton).setInterpolator(new LinearOutSlowInInterpolator()).setListener(null).start();
                    }
    
                    @Override
                    public void onAnimationCancel(View view) {
    
                    }
                })
                .start();
    

    In short, you need to add .setListener(null) to building animation in onAnimationEnd callback