48

Ok, whenever I try to replace a fragment in my application, it only adds the fragment inside of the container the other fragment is, and leaves the current fragment. I've tried calling replace and referencing the view the contains the fragment, and by referencing the fragment itself. Neither of these work. I can add a fragment to a view with the fragment transaction manager, but even if I try to remove it after its been added, it doesn't work. Any help would be appreciated. Here are my files.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    //Setup the actionabar. Use setDrawableBackground to set a background image for the actionbar.
    final ActionBar actionbar = getActionBar();
    actionbar.setDisplayShowTitleEnabled(false);
    actionbar.setDisplayUseLogoEnabled(true);
    actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    actionbar.addTab(actionbar.newTab().setText(R.string.home_tab_text).setTabListener(this),true);
    actionbar.addTab(actionbar.newTab().setText(R.string.insert_tab_text).setTabListener(this));

    Fragment fragment = new insert_button_frag();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.button_fragment, fragment);
    transaction.addToBackStack(null);

    transaction.commit();
}

Here is the layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <LinearLayout
        android:orientation="vertical"
        android:id="@+id/button_fragment_container"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <fragment
            android:name="com.bv.silveredittab.home_button_frag"
            android:id="@+id/button_fragment"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <fragment
            android:name="com.bv.silveredittab.quick_insert_frag"
            android:id="@+id/quick_insert_frag"
            android:layout_width="350dip"
            android:layout_height="fill_parent" />

        <fragment
            android:name="com.bv.silveredittab.editor_frag"
            android:id="@+id/editor_frag"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

    </LinearLayout>

</LinearLayout>

And here is the fragment code

public class insert_button_frag extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        return inflater.inflate(R.layout.insert_buttons,container, false);
    }
}

Like I have said. I have tried referencing the fragments parent view to replace it, and the fragment itself (by id) and still, it only adds the new fragment, inside the containing view the original fragment is in.

JJD
  • 50,076
  • 60
  • 203
  • 339
Shaun
  • 5,483
  • 10
  • 40
  • 49

4 Answers4

37

I solved this by using a placeholder in my Layout and then attaching my Fragment to it at runtime.

Like you, if I instantiated my Fragment within my xml layout then the contents would remain visible after replacing it with another Fragment at runtime.

Damian
  • 8,062
  • 4
  • 42
  • 43
20

The problem is this line:

transaction.replace(R.id.button_fragment, fragment);

replace has two overloaded forms. In both of them, the first argument is the container of the Fragment to be replaced, not the Fragment itself. So, in your case, you need to call

transaction.replace(R.id.button_fragment_container, fragment);

edit: I see in your question that you have tried both. I have tested and verified the behavior. This appears to be a bug in the FragmentTransaction API.

Edit2: not a bug after all. You simply cannot replace fragments added statically in a layout file. You can only replace those you have added programmatically ( Android: can't replace one fragment with another )

Community
  • 1
  • 1
Brian Cooley
  • 11,622
  • 4
  • 40
  • 39
2

I had the same issue. Take a look at this link: Android Fragment Duplication

I wonder if the fact that you're passing the container into the inflater.inflate() method causes it to create the new fragment inside of the old one instead of a wholesale replace. I've been providing 'null' to my inflaters in the working version.

Here's the essentials from the version I have working...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
   View newFrag = new View(getActivity().getApplicationContext());
   newFrag = inflater.inflate(R.id.frag, null);
   return newFrag;
}
Community
  • 1
  • 1
Peter
  • 808
  • 2
  • 11
  • 17
  • I took a look at it, but I dont think it really applies to my situation. Im not populating my fragments progmatically. For some reason my code isnt finding the existing fragment, so its not replacing it. Its not even generating any errors or warnings. Its really confusing. – Shaun Mar 14 '11 at 01:43
  • Sorry that didn't help. I edited my answer re: the inflater, but if that doesn't help, I'd have to go troubleshoot it w/ the IDE. – Peter Mar 14 '11 at 01:49
  • I tried this return inflater.inflate(R.layout.home_buttons,null, false); but that didnt work either – Shaun Mar 14 '11 at 02:09
  • I ran some if statements on it, using the isAdded() and isVisible() functions. It says that it is added to the activity, but is not visible, when clearly it is visible cause I can see it. I wonder what is causing this... – Shaun Mar 14 '11 at 02:15
  • The remove function isnt working with any of my fragments... wth? I thought using the fragment transaction manager was going to be easy... – Shaun Mar 14 '11 at 02:18
  • Could it be that the inflater you're using inflate(int resource, ViewGroup root, boolean attachToRoot) is only taking the layout params from that container you're passing since attachToRoot is declared false? maybe try the simple inflate(int resource, ViewGroup root)? – Peter Mar 14 '11 at 02:32
  • No, for some reason, its only adding the fragments layout to the root layout, and not adding the fragment to the fragment manager, or the fragment list (however you would put it). Should I be overriding anything else besides onCreateView, to attach the fragment to something? – Shaun Mar 14 '11 at 02:46
  • If I take out the declaration of the fragment in the xml layout, and just add the fragment on the activitys onCreate function, it works fine that way. For some reason, if its declared in the layout, its just adding the layout, not the fragment. – Shaun Mar 14 '11 at 02:50
  • I'm not an expert, I'm just going through the differences in the way that I got it to work vs. what you've posted above. Another difference is that I declared a new LinearLayout, giving it the application context, then set that to the inflated view from the inflate(int, null) method. Could it be that the inflater is creating it, but not having a context in which to display it? – Peter Mar 14 '11 at 02:52
  • I dont know. It does seem like the fragment isnt making it to the context, only passing the layout. But, Im only implementing the onCreateView, and onCreate. It should automatically pass the fragment to the context or activity if its called. – Shaun Mar 14 '11 at 02:54
  • Well, if you add the context, and it still doesn't work, I'm out of ideas ;) I hope someone with more experience than me comes along to help out. – Peter Mar 14 '11 at 03:00
  • What do you mean by adding the context? Im kinda new at this, so the context still kinda confuses me. I managed to fix the problem by adding the fragment progmmaticaly, and taking the fragment out of the layout all together, but I feel that is a shotty way of doing it, and I had to disable the portrait orientation in the app, because it would re add the fragment. How can I keep it from readding the fragment. Im adding it on the onCreate wihin the main activity – Shaun Mar 14 '11 at 03:22
  • I think we're both new enough at this to be dangerous ;) All I mean by 'add the context' is that line I put at the top of the onCreateView override where I create a new view inside the Activity's context. Your custom insert_button_frag class seems to just return a view, but it's not in the context of your main activity. The other area I'd try poking around is to include the linear layout inside the inflated xml resource instead of the layout. I seem to remember something like that causing my duplication issue, where it would add the new fragment inside of LinearLayout inside of the original... – Peter Mar 14 '11 at 03:28
  • Ok, I will give what you added a shot. I had to restart eclispe because it glitched up real bad. I think im done for tonight though, its irritated me enough for one day, lol. This is the first really irritating problem ive had to honeycomb, so I cant really complain. The rest of my app works fine. – Shaun Mar 14 '11 at 03:33
  • Oh, the inserts button xml file is the layout of the fragment im tryign to replace the home_buttons fragment with – Shaun Mar 14 '11 at 03:45
0

The google developer guide is quite confusing because it shows first to implement hard coded fragment in you xml. But if you really want to replace existing fragment with the new one, then you should add it(the first) at runtime.

What official site state is

FragmentTransaction replace (int containerViewId, Fragment fragment, String tag)

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.

You should noticed that it says that Replace an existing fragment that was added to container

So, your xml should look like someActivity.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    tools:context="someActivity">

    <ImageView
        .../>

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="bottom"/>

</RelativeLayout>

And than in your activity do in OnCreate()

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        [...]
        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, new FirstFragment).commit();

    }

Than you may easily replace fragment with your animations and event add it to back stack in order to save its state.

private SecondFrag getSecondFrag(){
    if(secondFrag == null)
        secondFrag = new SecondFrag()
    return secondFrag;
}

private void openRechargeFragment(){
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(R.anim.show_frag, R.anim.hide_frag,
                R.anim.show_frag, R.anim.hide_frag);
        ft.replace(R.id.fragment_container, getSecondFrag(), "myTAG");
        ft.addToBackStack(null);
        ft.commit();
}
murt
  • 3,790
  • 4
  • 37
  • 48