Animate a view from one Layout to other Layout

I recently did animation of a similar kind using Animators. In general, views will not display themselves outside of their parents' boundaries, the view will be cut by it's parent's boundaries. That's why, the trick is to place a new view (shuttleView) on top of the origin view (fromView) that you want to animate, align them, and animate scaling/translation of shuttleView into a target view (toView).

This solution supports both scaling and translation, here is sample: https://www.dropbox.com/s/iom95o93076h52f/device-2016-06-03-111557.mp4?dl=0

Here is the code:

activity_main.xml

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="90dp"
    android:layout_alignParentTop="true"
    android:background="@android:color/holo_blue_dark">

    <TextView
        android:id="@+id/itemTo"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_margin="10dp"
        android:background="@android:color/holo_blue_bright"
        android:text="to"/>

</LinearLayout>


<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="90dp"
    android:layout_alignParentBottom="true"
    android:background="@android:color/holo_blue_dark">

    <TextView
        android:layout_width="90dp"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:background="@android:color/holo_blue_bright" />

    <TextView
        android:id="@+id/itemFrom"
        android:layout_width="90dp"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:text="from"
        android:background="@android:color/holo_blue_bright" />

    <TextView
        android:layout_width="90dp"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:background="@android:color/holo_blue_bright" />
</LinearLayout>

<View
    android:id="@+id/shuttle"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:background="@android:color/holo_blue_bright"/>

Activity class:

public class MainActivity extends AppCompatActivity {
    public static final int ANIMATION_SPEED = 3000;
    private RelativeLayout rootView;
    private View fromView, toView, shuttleView;

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

        rootView = (RelativeLayout) findViewById(R.id.rootView);
        fromView = findViewById(R.id.itemFrom);
        toView = findViewById(R.id.itemTo);
        shuttleView = findViewById(R.id.shuttle);

        fromView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Rect fromRect = new Rect();
                Rect toRect = new Rect();
                fromView.getGlobalVisibleRect(fromRect);
                toView.getGlobalVisibleRect(toRect);

                AnimatorSet animatorSet = getViewToViewScalingAnimator(rootView, shuttleView, fromRect, toRect, ANIMATION_SPEED, 0);

                animatorSet.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        shuttleView.setVisibility(View.VISIBLE);
                        fromView.setVisibility(View.INVISIBLE);
                    }

                    @Override
                    public void onAnimationEnd(Animator animation) {
                        shuttleView.setVisibility(View.GONE);
                        fromView.setVisibility(View.VISIBLE);
                    }

                    @Override
                    public void onAnimationCancel(Animator animation) {

                    }

                    @Override
                    public void onAnimationRepeat(Animator animation) {

                    }
                });
                animatorSet.start();
            }
        });
    }


    public static AnimatorSet getViewToViewScalingAnimator(final RelativeLayout parentView,
                                                           final View viewToAnimate,
                                                           final Rect fromViewRect,
                                                           final Rect toViewRect,
                                                           final long duration,
                                                           final long startDelay) {
        // get all coordinates at once
        final Rect parentViewRect = new Rect(), viewToAnimateRect = new Rect();
        parentView.getGlobalVisibleRect(parentViewRect);
        viewToAnimate.getGlobalVisibleRect(viewToAnimateRect);

        viewToAnimate.setScaleX(1f);
        viewToAnimate.setScaleY(1f);

        // rescaling of the object on X-axis
        final ValueAnimator valueAnimatorWidth = ValueAnimator.ofInt(fromViewRect.width(), toViewRect.width());
        valueAnimatorWidth.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // Get animated width value update
                int newWidth = (int) valueAnimatorWidth.getAnimatedValue();

                // Get and update LayoutParams of the animated view
                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) viewToAnimate.getLayoutParams();

                lp.width = newWidth;
                viewToAnimate.setLayoutParams(lp);
            }
        });

        // rescaling of the object on Y-axis
        final ValueAnimator valueAnimatorHeight = ValueAnimator.ofInt(fromViewRect.height(), toViewRect.height());
        valueAnimatorHeight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // Get animated width value update
                int newHeight = (int) valueAnimatorHeight.getAnimatedValue();

                // Get and update LayoutParams of the animated view
                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) viewToAnimate.getLayoutParams();
                lp.height = newHeight;
                viewToAnimate.setLayoutParams(lp);
            }
        });

        // moving of the object on X-axis
        ObjectAnimator translateAnimatorX = ObjectAnimator.ofFloat(viewToAnimate, "X", fromViewRect.left - parentViewRect.left, toViewRect.left - parentViewRect.left);

        // moving of the object on Y-axis
        ObjectAnimator translateAnimatorY = ObjectAnimator.ofFloat(viewToAnimate, "Y", fromViewRect.top - parentViewRect.top, toViewRect.top - parentViewRect.top);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setInterpolator(new DecelerateInterpolator(1f));
        animatorSet.setDuration(duration); // can be decoupled for each animator separately
        animatorSet.setStartDelay(startDelay); // can be decoupled for each animator separately
        animatorSet.playTogether(valueAnimatorWidth, valueAnimatorHeight, translateAnimatorX, translateAnimatorY);

        return animatorSet;
    }
}

You can do a whole bunch of customizations in terms of what appears and disappears at different stages of animation in animatorSet listener. Hope it's helpful.


If someone is looking for simpler solution, you can use transitions framework: https://developer.android.com/training/transitions/index.html

To animate translation from one parent view to another, there is special transition ChangeTransform: https://developer.android.com/reference/android/transition/ChangeTransform.html

And a small example:

animatedView = View.inflate(ActivityMain.this, R.layout.item, firstParent);
animatedView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
Transition move = new ChangeTransform()
                        .addTarget(animatedView)
                        .setDuration(2000));
TransitionManager.beginDelayedTransition(rootParent, move);
firstParent.removeView(animatedView);
animatedView.setPadding(2,2,2,2);
animatedView.setElevation(4);
secondParent.addView(animatedView, 0);