I am trying to create a Fragment which has a public method for adding child Fragments to itself.
I've been reading through potentially similar questions but haven't found anything to help so far. I've reduced the problem down to a simple test app shown below.
Once fragA
has been added to the main layout, I call the public method fragA.addFragB()
to get it to add an instance of FragmentClassB to itself, but this causes the test app to crash, indicating "Activity has been destroyed" (see LogCat at the end of the post). Does this mean fragA
has been destroyed so I can't add fragB
to it, or does it mean fragB
has been destroyed so I can't add it to fragA
? Or does it mean something else entirely?
MainActivity.java
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragMan = getSupportFragmentManager();
// add Fragment A to the main linear layout
FragmentTransaction fragTrans = fragMan.beginTransaction();
FragmentClassA fragA = new FragmentClassA();
fragTrans.add(R.id.mainLinearLayout, fragA);
fragTrans.addToBackStack("A");
fragTrans.commit();
// get Fragment A to add a Fragment B to itself
fragA.addFragB();
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<LinearLayout
android:id="@+id/mainLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:orientation="horizontal" >
</LinearLayout>
</RelativeLayout>
FragmentClassA.java
public class FragmentClassA extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
public void addFragB() {
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
FragmentClassB fragB = new FragmentClassB();
childFragTrans.add(R.id.fragA_LinearLayout, fragB);
childFragTrans.addToBackStack("B");
childFragTrans.commit();
}
}
fragment_a.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragA_LinearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
FragmentClassB.java
public class FragmentClassB extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
}
fragment_b.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
LogCat
11-18 16:17:05.627: E/AndroidRuntime(14351): FATAL EXCEPTION: main
11-18 16:17:05.627: E/AndroidRuntime(14351): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.nestedfragmenttest/com.example.nestedfragmenttest.MainActivity}: java.lang.IllegalStateException: Activity has been destroyed
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.ActivityThread.access$600(ActivityThread.java:141)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.os.Handler.dispatchMessage(Handler.java:99)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.os.Looper.loop(Looper.java:137)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.ActivityThread.main(ActivityThread.java:5103)
11-18 16:17:05.627: E/AndroidRuntime(14351): at java.lang.reflect.Method.invokeNative(Native Method)
11-18 16:17:05.627: E/AndroidRuntime(14351): at java.lang.reflect.Method.invoke(Method.java:525)
11-18 16:17:05.627: E/AndroidRuntime(14351): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
11-18 16:17:05.627: E/AndroidRuntime(14351): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-18 16:17:05.627: E/AndroidRuntime(14351): at dalvik.system.NativeStart.main(Native Method)
11-18 16:17:05.627: E/AndroidRuntime(14351): Caused by: java.lang.IllegalStateException: Activity has been destroyed
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1365)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
11-18 16:17:05.627: E/AndroidRuntime(14351): at com.example.nestedfragmenttest.FragmentClassA.addFragB(FragmentClassA.java:26)
11-18 16:17:05.627: E/AndroidRuntime(14351): at com.example.nestedfragmenttest.MainActivity.onCreate(MainActivity.java:25)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.Activity.performCreate(Activity.java:5133)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
11-18 16:17:05.627: E/AndroidRuntime(14351): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
11-18 16:17:05.627: E/AndroidRuntime(14351): ... 11 more
EDIT:
ianhanniballake's answer and GrIsHu's later answer which expands on the same point have both been helpful in pointing out the root of the problem. However this raises a further issue.
The final intention is that FragmentClassA will be part of a library. It will be used in multiple situations and the number of FragmentClassB instances will vary, or there may even be none. Hence I need to be able to trigger the addition of the child fragments to any instance of FragmentClassA from the parent activity. I've just had a look at keeping fragA
as a class level variable in MainActivity
and then calling fragA.AddFragB()
in the MainActivity
's onActivityCreated()
method, but it is not available to be overridden. Any thoughts?
You can add your fragment to the activity's view hierarchy either by defining the fragment in your activity's layout file or by defining a fragment container in your activity's layout file and then programmatically adding the fragment from within your activity.
There are two ways to add a fragment to an activity: dynamically using Java and statically using XML. Before embedding a "support" fragment in an Activity make sure the Activity is changed to extend from FragmentActivity or AppCompatActivity which adds support for the fragment manager to all Android versions.
Use replace() to replace an existing fragment in a container with an instance of a new fragment class that you provide. Calling replace() is equivalent to calling remove() with a fragment in a container and adding a new fragment to that same container. transaction. commit();
To pass data from child to parent fragment, should follow the same way by using childFragmentManager instead parentFragmentManager when set result listener to the parent fragment. Now, same as the previous example, child fragment should produce the result with the same requestKey.
You can not directly load the fragment which you have declared in FragA
. The FragmentA will be loaded first and then after you can load the FragmentB by calling the method addFragB()
from your fragA's
onCreateView()
method.
Try out as below:
Remove the line fragA.addFragB();
from your MainActivity
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragMan = getSupportFragmentManager();
// add Fragment A to the main linear layout
FragmentTransaction fragTrans = fragMan.beginTransaction();
FragmentClassA fragA = new FragmentClassA();
fragTrans.add(R.id.mainLinearLayout, fragA);
fragTrans.addToBackStack("A");
fragTrans.commit();
}
}
And try to load the FragmentB
from FragmentA
as below:
public class FragmentClassA extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
addFragB(); //Call and load fragment B here.
return inflater.inflate(R.layout.fragment_a, container, false);
}
public void addFragB() {
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
FragmentClassB fragB = new FragmentClassB();
childFragTrans.add(R.id.fragA_LinearLayout, fragB);
childFragTrans.addToBackStack("B");
childFragTrans.commit();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With