Bottom sheet stays on screen when activity is recreated

Viewed 3287

I added a BottomSheet to my application. I hide it on startup in onCreate method. Later on it's shown only when needed. The issue I encountered is when user exists the app (with the bottom sheet expanded) and later comes back to the app, the Activity is recreated and the bottom sheet stays on the screen - even if it set it to hidden in onCreate.

BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN

The only solution I've found so far is to override onRestoreInstanceState callback, and don't let the Activity to restore it's state.

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    // FIXME Temporary fix for BottomSheet not hiding on app recreate
    //super.onRestoreInstanceState(savedInstanceState)
}

I'm sure there must be a better solution. What may be causing this problem?

Layout definition:

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

    <androidx.constraintlayout.widget.ConstraintLayout 
      android:id="@+id/myBottomSheet"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="@android:color/white"
      app:behavior_hideable="true"
      app:behavior_peekHeight="90dp"
      app:layout_behavior="@string/bottom_sheet_behavior">

</androidx.coordinatorlayout.widget.CoordinatorLayout>
2 Answers

While the answer posted by Amos Korir is completely correct, the main issue in my case was the android:id="@+id/root" assigned to CoordinatorLayout (Amos's solution doesn't take that into account).

Assigning an id to a view means when onSaveInstanceState is called, Android is going to save state of given view. In this case the bottom sheet is also saved and once the activity is recreated it shows on the screen.

Even though the activity goes through the full lifecycle, you simply can't hide it in onCreate, because bottom sheet state is restored in onRestoreInstanceState

So the easy solution to this issue is to add saveEnabled to your bottom sheet's parent.

android:saveEnabled="false"

Looks like it doesn't work when you add this line directly to the bottom sheet view. Keep in mind that it won't save state of other views too.

The best solution for me is to hide the bottom sheet in onRestoreInstanceState. It doesn't affect other views saved state.

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN
}

This is a full Kotlin activity trying to achieve the behavior. I have tested it by closing the application (leaving the application running in background then ) then opening it again.

class MainActivity : AppCompatActivity() {

 override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN

hideBottomSheet.setOnClickListener {
  BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN
}

showBottomSheet.setOnClickListener {
  BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_EXPANDED
     }
 }



   // this will work depending on the behaviour you want for your users
  override fun onResume() {
    super.onResume()
  //BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN
  }



   // This is enough for you, according to your description
  override fun onStart() {
    super.onStart()
    BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN
  }

  // this will work depending on the behaviour you want for your users
  override fun onRestart() {
    super.onRestart()
    // BottomSheetBehavior.from(myBottomSheet).state = BottomSheetBehavior.STATE_HIDDEN
  }
}

Here I am using the activity lifecycle methods.

Below is my Xml file, I have added a few button for test control.

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >

 <androidx.constraintlayout.widget.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:id="@+id/myBottomSheet"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="@android:color/holo_blue_bright"
      app:behavior_hideable="true"
      app:behavior_peekHeight="90dp"
      app:layout_behavior="@string/bottom_sheet_behavior"/>

  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="horizontal"
      >


<Button
    android:id="@+id/showBottomSheet"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
      android:text="Show"
        />
    <Button
        android:id="@+id/hideBottomSheet"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="hide"
        />
  </LinearLayout>


</androidx.coordinatorlayout.widget.CoordinatorLayout>
Related