Replacing a fragment with another fragment of the same class
Solution 1
Bypass Solution
Explanation (hoover to see it)
Since the source code for. Thanks to @devconsole for pointing out the source code. I know now why this happens. ThefragmentTransaction.replace/add/remove
is not available I could not find what really happens. But it is reasonable to think that at some point it compares the current class name with the replacement class name and it exits if they are the same.FragmentManager.removeFragment()
method does not reset the fragment state, it remains RESUMED, then the methodmoveToState(CREATED)
only initilizes a fragmentif (f.mState < newState)
=if (RESUMED < CREATED)
=false
. Else, ergo, it just resumes the fragment.
So to solve this problem I created an almost empty fragment which only purpose is to replace itself with the target fragment.
public class JumpFragment {
public JumpFragment() {
}
@Override
public void onStart() {
Bundle data = getArguments();
int containerId = data.getString("containerID");
String tag = data.getString("tag");
//Class<?> c = data.get???("class");
//Fragment f = (Fragment) c.newInstance();
Fragment f = (Fragment) new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
f.setArguments(data);
fragmentTransaction.replace(containerId, f, tag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
And I use it:
data.setInt("Layout index",i);
data.setInt("containerID",R.id.fragmentContent);
data.setString("tag","MY");
fragmentTab0 = (Fragment) new JumpFragment();
fragmentTab0.setArguments(data);
fragmentTransaction.replace(R.id.fragmentContent, fragmentTab0);
fragmentTransaction.commit();
Now no fragment is replaced by a same class fragment:
MyFragment -> JumpFragment -> MyFragment
I haven't figured out how to pass the class through the arguments bundle, to make it totally generic.
Solution 2
The following worked without problems for me. Notice that I used the Android Support Library.
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"
tools:context=".MainActivity" >
<Button
android:id="@+id/button_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ONE" />
<Button
android:id="@+id/button_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/button_one"
android:text="TWO" />
<FrameLayout
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/button_one" >
</FrameLayout>
</RelativeLayout>
MainActivity.java
:
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment fragment = DetailsFragment.newInstance("INITIAL");
transaction.add(R.id.main_container, fragment);
transaction.commit();
}
findViewById(R.id.button_one).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
update("Button 1 clicked");
}
});
findViewById(R.id.button_two).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
update("Button 2 clicked");
}
});
}
protected void update(String value) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment fragment = DetailsFragment.newInstance(value);
transaction.replace(R.id.main_container, fragment);
transaction.commit();
}
public static final class DetailsFragment extends Fragment {
public static DetailsFragment newInstance(String param) {
DetailsFragment fragment = new DetailsFragment();
Bundle args = new Bundle();
args.putString("param", param);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView textView = new TextView(container.getContext());
textView.setText(getArguments().getString("param"));
return textView;
}
}
}
ilomambo
I have a solid programming education. Trying to learn how everything works and interacts. Hopefully, to write great applications.
Updated on June 13, 2022Comments
-
ilomambo almost 2 years
I have a
fragment
(let's call it MyFragment) that inflates different layouts according to a parameter passed in the arguments.
All works well if MyFragment is started from a different fragment. But if MyFragment is active and I want to launch a new MyFragment with a different layout parameter, thefragmentManager
does not create a new fragment at all.data.setInt("Layout index",i); fragmentTab0 = (Fragment) new MyFragment(); fragmentTab0.setArguments(data); fragmentTransaction.replace(R.id.fragmentContent, fragmentTab0, "MY"); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit();
How can I
forceconvince thefragmentTransaction
to launch the fragment again?NOTE: The important point here is I need to inflate again a layout, which is different from the layout inflated before. The code looks like:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { switch( getArguments().getInt("Layout index") ) { case 1: rootView = inflater.inflate(R.layout.firstlayout, container, false); break; case 2: rootView = inflater.inflate(R.layout.secondlayout, container, false); break; case 3: rootView = inflater.inflate(R.layout.thirdlayout, container, false); break; default: break; }
-
ilomambo almost 11 yearsI used
replace
which according to the documentation:Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.
-
Sébastien Morand almost 11 yearsyeah, that should do the trick... Just try
remove
maybe the implementation is a bit different. -
ilomambo almost 11 yearsNot exactly what I need. As I wrote, I inflate different layouts based on an argument passed on the fragment data bundle. your example inflates only once.
-
ilomambo almost 11 yearsI tried splitting
replace
intoremove
+add
, but still the same behavior -
devconsole almost 11 yearsRegarding the source code: Class BackStackRecord extends FragmentTransaction.
-
ilomambo almost 11 years@devconsole I think you got it backwards.
BackstackRecord
extendsFragmentTransaction
. It is not theFragmentTransaction
implementation. -
devconsole almost 11 years
FragmentTransaction
is an abstract class,BackStackRecord
extends that class, thereby providing an implementation. See also FragmentManager line 468:FragmentManager.beginTransaction()
actually returns aBackStackRecord
. -
ilomambo almost 11 years@devconsole Thanks, I found the reason to the problem and added an explanation.
-
devconsole almost 11 yearsI still cannot reproduce the problem on my Nexus 4 running Android 4.2.2. I tried both the native APIs and the Android Support Library, my example works in both cases, see my answer. Maybe the problem was fixed in a version <= 4.2.2?
-
ilomambo almost 11 years@devconsole I tried your answer code, and it works on the emulator with android17 as target. I even inflated two different layouts based on arguments, as I described here, and it worked, I added the transaction to the back stack, and it worked. I dropped v4.support and used android classes, and it worked. At this point myheadache is big as a house, too bad the debugger in Eclispe refuses to follow the code into the fragmentManager. I need to figure out what is really happening.
-
ilomambo almost 11 years
-
nafsaka over 8 yearsI've facing the same problem. I try this, but doesn't work. Are there any solution in this?