The problem may be coming from my architecture but I'm not too sure.
Here's the problem.
I have an activity which initiate a fragment. The .xml
file of that activity contains, a FrameLayout
for the fragment (let's call it FragmentA) and a CoordinatorLayout
which acts as a bottomSheet.
Inside the bottomSheet, there's another FrameLayout
for an another fragment (let's call it FragmentB).
What I'm trying to accomplish is that when the bottomSheet is expanded all the way, then, on its FrameLayout
, I initiate FragmentB.
To save space, I decided to handle everything related to the bottomSheet in a different, separate class, named BottomSheetManagement
I, then, attached BottomSheetCallback
on my bottomSheet and when its state is expanded
, I call the method initiateFragment(currentFragmentID, WorkFragment.newInstance())
.
This method is created in an interface called FragmentHost
which is implemented in my mainActivity. Thus, when I call initiateFragment(currentFragmentID, WorkFragment.newInstance())
, I'm actually calling the method of the same name in my mainActivity.
However, when I test my app and drag my bottomSheet to the top, i get the error message : IllegalStateException : Activity has been destroyed.
I don't understand how since in initiateFragment(currentFragmentID, WorkFragment.newInstance())
, I make sure that the activity is still there (through !isfinishing() && !isDestroyed()
)
What can I do?
MainActivity.java
public class MainActivity extends AppCompatActivity implements FragmentHost {
//BottomSheet Variables
View bottomSheet;
RelativeLayout bottomSheetHeader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow();
}
//Handle BottomSheet
bottomSheet = findViewById(R.id.bottom_sheet);
bottomSheetHeader = findViewById(R.id.BottomSheet_Layout_Header);
bottomSheetManagement();
}
private void bottomSheetManagement(){
BottomSheetManagement bottomSheetManagement = new BottomSheetManagement();
bottomSheetManagement.handleBottomSheet(R.id.work_container, bottomSheet, bottomSheetHeader);
}
@Override
public void initiateFragment(int containerID, Fragment fragment) {
if (!isFinishing() && !isDestroyed()){
getSupportFragmentManager().beginTransaction()
.replace(containerID, fragment)
.commit();
}
}
}
BottomSheetManagement.java
public class BottomSheetManagement {
private BottomSheetBehavior bottomSheetBehavior;
private int activeBottomSheetState;
public void handleBottomSheet(int currentFragmentID, View bottomSheet, RelativeLayout bottomSheetHeader){
FragmentHost fragmentHost = new MainActivity();
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetHeader.setOnClickListener(v -> {
switch (bottomSheetBehavior.getState()){
case BottomSheetBehavior.STATE_COLLAPSED:
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
break;
case BottomSheetBehavior.STATE_EXPANDED:
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
break;
}
});
bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
switch (newState){
case BottomSheetBehavior.STATE_EXPANDED:
fragmentHost.initiateFragment(currentFragmentID, WorkFragment.newInstance());
break;
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
}
}
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_hideable="false"
app:behavior_peekHeight="50dp"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:id="@+id/BottomSheet_Layout_Header">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/app_name"
style="?attr/titleTextAppearance"
android:textColor="?attr/colorOnSurface"/>
</RelativeLayout>
<FrameLayout
android:id="@+id/work_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
You should assign the context of MainActivity already created to fragmentHost instead of creating a new context which you are doing by writing 'new MainActivivty()' which destroys previous MainActivity from where you called BottomSheetManagement and creates a new one. Modify your BottomSheetManagement code to,
public class BottomSheetManagement {
private BottomSheetBehavior bottomSheetBehavior;
private int activeBottomSheetState;
private FragmentHost fragmentHost;
public void handleBottomSheet(FragmentHost context, int currentFragmentID, View bottomSheet, RelativeLayout bottomSheetHeader){
fragmentHost = (FragmentHost) context;
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetHeader.setOnClickListener(v -> {
and in MainActivity code, change method to
private void bottomSheetManagement(){
BottomSheetManagement bottomSheetManagement = new BottomSheetManagement();
bottomSheetManagement.handleBottomSheet(this, R.id.work_container, bottomSheet, bottomSheetHeader);
}