Search code examples
androidandroid-fragmentsandroid-animationfragmenttransaction

Fragment Removed from View Hierarchy at Start of Animation


I have a fragment placed between two other views that I need to transition out of view smoothly. I have set up a fragment animation to scale the fragment out of view. This works fine. However, the fragment's view is immediately taken out of the view hierarchy as soon as the animation starts, causing the view below the fragment to collapse over the fragment's view. How can I get the view below the fragment to transition up and down smoothly with the scaling of the fragment?

Here is an example of what happens:

enter image description here

Here is MainActivity.java

public class MainActivity extends AppCompatActivity {

public final String TAG = "MainActivity";

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

    Button btnHide = findViewById(R.id.btn_hide);
    Button btnShow = findViewById(R.id.btn_show);

    final FragmentManager fm = getSupportFragmentManager();
    final Fragment fragment = fm.findFragmentById(R.id.frag_middle);

    btnHide.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(fragment != null) {
                FragmentTransaction ft = fm.beginTransaction();
                ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
                ft.hide(fragment);
                ft.commit();
            }else{
                Log.d(TAG, "fragment is null!");
            }
        }
    });

    btnShow.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(fragment != null){
                FragmentTransaction ft = fm.beginTransaction();
                ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
                ft.show(fragment);
                ft.commit();
            }else{
                Log.d(TAG, "fragment is null!");
            }
        }
    });

}

}

Here is activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
tools:context=".MainActivity">

<TextView
    android:id="@+id/tv_top"
    android:layout_height="wrap_content"
    android:layout_width="0dp"
    android:layout_marginStart="20dp"
    android:layout_marginEnd="20dp"
    android:padding="16dp"
    android:text="TextViewTop"
    android:textAlignment="center"
    android:textStyle="bold"
    android:textColor="@android:color/white"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    android:background="@color/colorPrimary"/>

<fragment
    android:id="@+id/frag_middle"
    android:name="my.packagename.mytestapplication.TestFragment"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/tv_top"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"/>

<TextView
    android:id="@+id/tv_bottom"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="20dp"
    android:layout_marginEnd="20dp"
    android:padding="16dp"
    android:text="TextViewBottom"
    android:textAlignment="center"
    android:textStyle="bold"
    android:textColor="@android:color/white"
    app:layout_constraintTop_toBottomOf="@+id/frag_middle"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    android:background="@color/colorPrimary"/>

<Button
    android:id="@+id/btn_hide"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hide Fragment"
    app:layout_constraintTop_toBottomOf="@id/tv_bottom"
    app:layout_constraintStart_toStartOf="parent"
    />

<Button
    android:id="@+id/btn_show"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Show Fragment"
    app:layout_constraintTop_toBottomOf="@id/tv_bottom"
    app:layout_constraintEnd_toEndOf="parent"/>

Here is an example of the animation xml:

<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000">

<scale
    android:fromYScale="0"
    android:toYScale="1"
    android:fromXScale="1"
    android:toXScale="1" />

Any help would be greatly appreciated! Thanks


Solution

  • There is easier way to implement this animation with Transition API. TransitionManager can handle and animate view or view hierarchy changes. When you hide/show fragment you just change visibility of fragment view.

    private void toggle() {
        final FragmentManager fm = getSupportFragmentManager();
        final Fragment fragment = fm.findFragmentById(R.id.frag_middle);
        ViewGroup parentViewGroup = findViewById(R.id.parentViewGroup);
    
        TransitionManager.beginDelayedTransition(parentViewGroup, new AutoTransition());
        FragmentTransaction ft = fm.beginTransaction();
        if (show) {
            ft.show(fragment);
        } else {
            ft.hide(fragment);
        }
        ft.commitNow();
    
        show = !show;
    }
    

    Important thing here is to use commitNow method instead of commit. Because commitNow execute fragment transaction immediately and thats why TransitionManager handle view changes. With commit it doesnt work because commit executes transaction asynchronously.

    <android.support.constraint.ConstraintLayout 
        ...
        android:id="@+id/parentViewGroup">
    
        <fragment
            android:id="@+id/frag_middle"
            android:name="TestFragment"
            android:layout_width="300dp"
            android:layout_height="200dp"
            app:layout_constraintTop_toBottomOf="@id/tv_top"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>
    
    </android.support.constraint.ConstraintLayout>
    

    I've add width and height to fragment in this xml. Here is result:

    enter image description here