0
votes

I have two layouts (green on top, red on bottom) in a vertical LinearLayout (parent) looking similar to this:

.

When focus goes from the green to red, I would like the green to slide up off the screen and have the red simultaneously slide up with it and fill the whole screen. And when focus moves from red back up I want the green to slide back into the screen and return to the original configuration. I have tried looking at many other questions but none have had the solution I need. I tried just changing visibility between gone and visible but I want it to be a smooth animation. I've tried using parentLayout.animate().translationY(greenLayout.getHeight()) on the outer LinearLayout and that does give the animation I want but then the red does not expand to fill the screen, like this:

.

I know this question is similar to this one but that question is really old and only had one answer which didn't work for me.

1
Is the second picture posted what is happening or what you want to have happen? And how does the user get focus back onto the green view once it's animated off-screen? - Ben P.
In the second picture I'm showing how after the green goes away the red doesn't expand to fill the screen but maintains its original height. And focus returns by clicking up from the red. - Omar Issa

1 Answers

0
votes

My solution has a lot of different pieces, so I'll start with the full XML and java code, and then talk about the important bits:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <View
        android:id="@+id/green"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#0f0" />

    <View
        android:id="@+id/red"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#f00"/>

</LinearLayout>

In the XML, the only really important part is that the red view uses a height of 0dp and weight of 1. This means it takes up all extra vertical space, which will be important when we get rid of the green view.

public class MainActivity extends AppCompatActivity {

    private int originalHeight;

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

        final View green = findViewById(R.id.green);
        final View red = findViewById(R.id.red);

        green.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                green.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                originalHeight = green.getHeight();
            }
        });

        green.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                animateHeightOfView(green, originalHeight, 0);
            }
        });

        red.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                animateHeightOfView(green, 0, originalHeight);
            }
        });
    }

    private void animateHeightOfView(final View view, int start, int end) {
        ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int height = (int) valueAnimator.getAnimatedValue();
                ViewGroup.LayoutParams params = view.getLayoutParams();
                params.height = height;
                view.setLayoutParams(params);
            }
        });
        animator.start();
    }
}

In the Java, the two main parts are the ViewTreeObserver.OnGlobalLayoutListener and the animateHeightOfView() method.

The OnGlobalLayoutListener exists to capture the green view's original height. We have to use a listener to do this instead of just writing originalHeight = green.getHeight() inside onCreate() because the green view isn't actually laid out at that point, so getHeight() would return 0 if we tried that.

The animateHeightOfView() method leverages the ValueAnimator class to animate the height of whatever view you pass to it. Since there's no direct setter for a view's height, we can't use simpler methods like .animate(). We set up the ValueAnimator to produce int values on every frame, and then we use a ValueAnimator.AnimatorUpdateListener to modify the view's LayoutParams to set the height.

Feel free to play with it. I'm using click listeners to trigger the animation, and you mentioned focus, but you should be able to call animateHeightOfView() in a different way if it suits you.

enter image description here enter image description here