browep
browep

Reputation: 5107

siblings of Animated layout not animating till after animation done

I am using an Animator ( http://developer.android.com/reference/android/animation/Animator.html ) to add and remove views from a ViewGroup. When I remove them the view animates out ( it's "slide up" motion ) but the other children do not react (slide up to take up the space ) to the animation until it is done. I am presuming because it I am using not setting the animated View to GONE until after the animation. Is there a way to get the other views to slide up while the animated view is sliding out? Here is the relevant code

AnimatorListener mCloseListener = new AnimatorListener() {
        @Override public void onAnimationEnd(Animator arg0) {
            viewToAnimate.setVisibility(View.GONE);
        }
        @Override public void onAnimationStart(Animator arg0) { }
        @Override public void onAnimationCancel(Animator arg0) { }
        @Override public void onAnimationRepeat(Animator arg0) { }
    };

viewToAnimate.animate().translationYBy(-viewToAnimate.getMeasuredHeight())
            .setDuration(ANIMATION_DURATION).setInterpolator(new DecelerateInterpolator()).setListener(
                    mCloseListener);

Upvotes: 0

Views: 833

Answers (1)

sandrstar
sandrstar

Reputation: 12643

First of all You're not using Animator, but ViewPropertyAnimator, which is slightly different and You're trying to animate view translation. Looking at translation definition it become clear that it get applied post-layout, so I assume that other views / view gorup don't participate and don't care about that property value.

I believe, You need to animate margin or some other position of the view to make ViewGroup aware of configuration changes. I think it can be achieved with Property Animation, but needs little bit more coding.

Here's some trivial example. Say button add / remove by sliding up / down should be animated from the following layout:

<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="@dimen/button_height"
        android:layout_alignParentTop="true"
        android:id="@+id/btn1"
        android:layout_weight="1"
        android:text="Button 1"
        android:onClick="buttonClickListener" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="@dimen/button_height"
        android:layout_below="@id/btn1"
        android:id="@+id/btn2"
        android:layout_weight="1"
        android:text="Button 2: Remove button 1"
        android:onClick="buttonClickListener" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="@dimen/button_height"
        android:layout_below="@id/btn2"
        android:id="@+id/btn3"
        android:layout_weight="1"
        android:text="Button 3: Remove button 2"
        android:onClick="buttonClickListener" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="@dimen/button_height"
        android:layout_below="@id/btn3"
        android:id="@+id/btn4"
        android:layout_weight="1"
        android:text="Button 4: add removed button"
        android:onClick="buttonClickListener" />
</RelativeLayout>

Then activity might look like the following:

public class MyActivity extends Activity implements Animator.AnimatorListener {

    private static final String TAG = "MyActivity";

    /** Queue to manage removed buttons */
    private LinkedBlockingDeque<Integer> mRemovedButtonDeque = new LinkedBlockingDeque<Integer>();
    /** Distance view should be moved up and down, from resources */
    private int mMovingDistance = 0;
    /** Animators to be used */
    private ObjectAnimator mMarginAnimator;
    /** Duration of all animations */
    private static final int ANIMATION_DURATION = 1000;
    /** Id of currently adding view */
    private int mAddingId = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Show the layout with the test view
        setContentView(R.layout.main);

        mMovingDistance = getResources().getDimensionPixelSize(R.dimen.button_height);

        mMarginAnimator = new ObjectAnimator().setDuration(ANIMATION_DURATION);
        mMarginAnimator.setInterpolator(new DecelerateInterpolator());

        mMarginAnimator.addListener(this);
    }

    @Override
    public void onAnimationStart(final Animator animation) {
        // nothing to do here
    }

    @Override
    public void onAnimationEnd(final Animator animation) {
        if (mAddingId == 0) {
            final Integer removingViewId = mRemovedButtonDeque.peek();

            if (removingViewId != null) {
                findViewById(removingViewId).setVisibility(View.GONE);
            }
        }
    }

    @Override
    public void onAnimationCancel(final Animator animation) {
        // nothing to do here
    }

    @Override
    public void onAnimationRepeat(final Animator animation) {
        // nothing to do here
    }

    /**
     * Listener for buttons click
     *
     * @param clickedButton {@link View}
     */
    public void buttonClickListener(final View clickedButton) {
        final int buttonId = clickedButton.getId();
        MarginAnimatorHelper animator = null;

        switch (buttonId) {
            case R.id.btn1:
                // nothing to do here
                break;

            case R.id.btn2:
                animator = new MarginAnimatorHelper((Button) findViewById(R.id.btn1));
                mRemovedButtonDeque.add(R.id.btn1);
                mMarginAnimator.setIntValues(0, -mMovingDistance);
                break;

            case R.id.btn3:
                if (findViewById(R.id.btn1).getVisibility() != View.VISIBLE) {
                    animator = new MarginAnimatorHelper((Button) findViewById(R.id.btn2));
                    mRemovedButtonDeque.add(R.id.btn2);
                    mMarginAnimator.setIntValues(0, -mMovingDistance);
                }
                break;

            case R.id.btn4:
                final Integer removedId = mRemovedButtonDeque.pollLast();

                if (removedId != null) {
                    final Button removedButton = (Button) findViewById(removedId);

                    animator = new MarginAnimatorHelper(removedButton);
                    removedButton.setVisibility(View.VISIBLE);
                    mMarginAnimator.setIntValues(-mMovingDistance, 0);
                }
                break;
        }

        if (animator != null) {
            mMarginAnimator.setTarget(animator);
            mMarginAnimator.setPropertyName(MarginAnimatorHelper.PROPERTY_NAME);
            mMarginAnimator.start();
        }
    }

    /**
     * This view will manages view removing
     * and act as {@link ObjectAnimator} target
     */
    private static class MarginAnimatorHelper {
        /** Name of the property to be animated */
        private static final String PROPERTY_NAME = "topMargin";

        /** Button to be animated */
        private final Button mButton;

        /**
         * Default constructor
         *
         * @param button {@link Button} to be animated
         */
        public MarginAnimatorHelper(final Button button) {
            mButton = button;
        }

        /**
         * Setter for property 'topMargin'
         *
         * @param margin new value of the property
         */
        public void setTopMargin(final int margin) {
            final ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mButton.getLayoutParams();

            params.topMargin = margin;
            mButton.setLayoutParams(params);
        }
    }
}

There's inner class MarginAnimatorHelper which does all main work: changes real view layout parameters which affects other layout elements.

Upvotes: 1

Related Questions