0

I have a fragment with 3 LinearLayout inside. By default, the first one have the weight attribute to 1.

Each LinearLayout have a OnClickListener that trigger 2 animations:

  • Animation with a weight from 0 to 1 for the one that is clicked.
  • Animation with a weight from 1 to 0 for the one currently opened.

My problem is that sometime the animations are not finishing completely.

Here is the code triggered by the OnClickListener:

private void launchAnimation(final LinearLayout llToExpand) {
    ViewWeightAnimationWrapper animationWrapper = new ViewWeightAnimationWrapper(llToExpand);
    ObjectAnimator anim = ObjectAnimator.ofFloat(animationWrapper,
            "weight",
            animationWrapper.getWeight(),
            1f);
    anim.setDuration(1000);

    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            Log.d("Anim1", "onAnimationEnd: Anim1 have ended");
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            Log.d("Anim1", "onAnimationCancel: Anim1 have been canceled.");
        }
    });

    ViewWeightAnimationWrapper animationWrapper2 = new ViewWeightAnimationWrapper(mLlCurrentlyOpened);
    ObjectAnimator anim2 = ObjectAnimator.ofFloat(animationWrapper2,
            "weight",
            animationWrapper2.getWeight(),
            0f);
    anim2.setDuration(1000);

    anim2.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            Log.d("Anim2", "onAnimationEnd: Anim2 have ended");
            mLlCurrentlyOpened = llToExpand;
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            Log.d("Anim2", "onAnimationCancel: Anim2 have been canceled.");
        }
    });
    anim.start();
    anim2.start();
}

Here is the code of my ViewWeightAnimationWrapper:

public class ViewWeightAnimationWrapper {
private View view;

public ViewWeightAnimationWrapper(View view) {
    if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) {
        this.view = view;
    } else {
        throw new IllegalArgumentException("The view should be a LinearLayout");
    }
}

public void setWeight(float weight) {
    Log.i(String.valueOf(view.getId()), "setWeight: " + weight);
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
    params.weight = weight;
    view.requestLayout();
}

public float getWeight() {
    return ((LinearLayout.LayoutParams) view.getLayoutParams()).weight;
}

Here are the log that I have when the animation isn't going to the end:

I/2131755212: setWeight: 0.5
I/2131755207: setWeight: 0.5
I/2131755212: setWeight: 0.5251222
I/2131755207: setWeight: 0.47487777
I/2131755212: setWeight: 0.55174345
I/2131755207: setWeight: 0.44825655
D/Anim1: onAnimationCancel: Anim1 have been canceled.
D/Anim1: onAnimationEnd: Anim1 have ended
D/Anim2: onAnimationCancel: Anim2 have been canceled.
D/Anim2: onAnimationEnd: Anim2 have ended

I have no clue about why my animation are not canceled every time.

How can I know what is canceling my animations? Is it possible to force the animations to finish when they are cancelled?

double-beep
  • 5,031
  • 17
  • 33
  • 41

2 Answers2

0

I haven't found the reason about why the animation is canceled. On the documentation, they say that the animation is canceled when there the same animation is triggered on the same object, which wasn't my case.

However, i have found a workaround here that is giving me the expected result.

I have replaced the ObjectAnimator by a custom animation:

     /**
     * Launch the expand animation on the LinearLayout in parameter and launch the folding animation
     * on the currently opened LinearLayout.
     * @param llToExpand LinearLayout that we want to expand.
     */
    private void launchAnimation(final LinearLayout llToExpand) {

        // Create the and assign the animations
        ExpandAnimation expand = new ExpandAnimation(llToExpand, 0, 1);
        expand.setDuration(500);
        ExpandAnimation fold = new ExpandAnimation(mLlCurrentlyOpened, 1, 0);
        fold.setDuration(500);

        // Start the animations
        llToExpand.startAnimation(expand);
        mLlCurrentlyOpened.startAnimation(fold);

        // Switch the currently opened LinearLayout for the next time
        mLlCurrentlyOpened = llToExpand;
    }

Here is my ExpandAnimation:

public class ExpandAnimation extends Animation {

private float mStartWeight;
private final float mEndWeight;
private final LinearLayout mContent;

public ExpandAnimation(LinearLayout content, float startWeight,
                float endWeight) {
    mStartWeight = startWeight;
    mEndWeight = endWeight;
    mContent = content;
}

@Override
protected void applyTransformation(float interpolatedTime,
                                   Transformation t) {
    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContent
            .getLayoutParams();
    mStartWeight = lp.weight;
    lp.weight = (mStartWeight + ((mEndWeight - mStartWeight) * interpolatedTime));
    mContent.setLayoutParams(lp);
}

@Override
public boolean willChangeBounds() {
    return true;
}
}
Community
  • 1
  • 1
0

I just ran into what might be the same issue. Looking at the ObjectAnimator source, it's holding a WeakReference to the animation target. In my case I was using a dedicated object created only to perform the animation, and it turned out that the weak reference was the only reference. Thus it would sometimes be de-referenced during the animation. ObjectAnimator handles this "cleanly", simply canceling the animation.

tliebeck
  • 816
  • 1
  • 8
  • 19