Search code examples
androidandroid-animationandroid-transitions

Not seeing animations when using TransitionManager.go() to change scenes


Trying to create a splash screen with a logo in the middle, which then moves up to allow an EditText and Start button to appear on the screen.

I've set up sceneA and sceneB to provide the start and end states, respectively. The only difference between the scenes is the splash logo being constrained to parent bottom in sceneA, and to EditText top in sceneB.

Here is the code for the Main Activity.


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

        ViewGroup sceneRoot = findViewById(R.id.scene_root);
        Scene sceneA = Scene.getSceneForLayout(sceneRoot, R.layout.scene_a, this);
        Scene sceneB = Scene.getSceneForLayout(sceneRoot, R.layout.scene_b, this);

        TransitionManager.go(sceneB);

        editText = findViewById(R.id.editText);
        button = findViewById(R.id.button);

        editText.setVisibility(View.VISIBLE);
        editText.setAlpha(0f);
        editText.animate()
                .alpha(1f)
                .setDuration(animDuration)
                .setListener(null);

        button.setVisibility(View.VISIBLE);
        button.setAlpha(0f);
        button.animate()
                .alpha(1f)
                .setDuration(animDuration)
                .setListener(null);
    }

With this code in place as-is, the app will launch with everything in its final position, no animation for the movement. The visibility animations for the editText and Button work as expected.

Why aren't the scene transition animations triggering? I've tried passing in my own transitions to the TransitionManager, and nothing changes. It seems like the animations just aren't triggering, even though the scene changes seem to be happening.

Additionally, curiously, if I do this:

TransitionManager.go(sceneA);
TransitionManager.go(sceneB);

Then the final result is the layout of sceneA. The reverse is true if I swap the order of the functions (final result is sceneB). What is going on here? It seems like the first method blocks the second, even though I don't see any transitions happening.

Additional files used:

The main Layout file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainMenuMasterLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    tools:context=".main">

    <FrameLayout
        android:id="@+id/scene_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include layout="@layout/scene_a" />
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

sceneA:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainSceneContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/splashLogo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/logo_transparent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:layout_constraintTop_toBottomOf="@+id/splashLogo"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/button"
        />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/editText" />

</androidx.constraintlayout.widget.ConstraintLayout>

sceneB:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainSceneContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/splashLogo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/logo_transparent"
        app:layout_constraintBottom_toTopOf="@id/editText"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/editText" />

</androidx.constraintlayout.widget.ConstraintLayout>

Solution

  • With this code in place as-is, the app will launch with everything in its final position, no animation for the movement.

    It happens because you change scenes too early. In onCreate method your view (I mean R.layout.main) is not measured and showed yet, android do it much later. You need to wait until android create and show main view (with sceneA) and only after that start transition to sceneB. There is OnPreDrawListener which invoked when view is measured and about to be drawn.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
    
        View view = LayoutInflater.from(this).inflate(R.layout.main, null);
        setContentView(view);
    
        view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                view.getViewTreeObserver().removeOnPreDrawListener(this);
    
                TransitionManager.go(sceneB);
    
                return true;
            }
        });
    }