Replacing a Fragment in a Tab Layout with an ActionBar

In the post Tab Layout in Android with ActionBar and Fragment the fragments can’t be replaced later, in this post I write the changes in order to replace the first fragment of the first tab with a third fragment using a button at runtime.

  1. The project in Tab Layout in Android with ActionBar and Fragment is the starting point, and as first step you add new strings in the file /res/values/strings.xml
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">TabActionBar</string>
        <string name="label1">one</string>
        <string name="label2">two</string>
        <string name="label3">three</string>
        <string name="body1"> body one</string>
        <string name="body2">body two</string>
        <string name="body3">body three</string>
    </resources>
    
  2. edit the layout of the first fragment res/layout/tab1.xml to add a button that replaces the fragment
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tab1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" 
        android:gravity="center">
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/body1" />
    
        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="replaceFragment"
            android:text="open fragment 3" />
    
    </LinearLayout>
    
  3. create the layout of the layout for the third fragment res/layout/tab3.xml
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tab3"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" 
        android:gravity="center">
    
        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/body3" />
        
            <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="replaceFragment"
            android:text="open fragment 1" />
    
    </LinearLayout>
    
  4. create the class of the third fragment eu\lucazanini\Tab3Fragment.java
    package eu.lucazanini;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.LinearLayout;
    
    public class Tab3Fragment extends Fragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
    	    Bundle savedInstanceState) {
    	    
    	return (LinearLayout) inflater.inflate(R.layout.tab3, container, false);
        }
    
    }
    
  5. edit the main activity eu\lucazanini\TabActionBarActivity.java
    package eu.lucazanini;
    
    import java.lang.ref.WeakReference;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    import android.app.ActionBar;
    import android.app.ActionBar.Tab;
    import android.app.Activity;
    import android.app.Fragment;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.View;
    
    public class TabActionBarActivity extends Activity {
    
        private class TabListener<T extends Fragment> implements
    	    ActionBar.TabListener {
    	private final Activity mActivity;
    	private final Class<T> mClass;
    	private Fragment mFragment;
    	private final String mTag;
    
    	/**
    	 * Constructor used each time a new tab is created.
    	 * 
    	 * @param activity
    	 *            The host Activity, used to instantiate the fragment
    	 * @param tag
    	 *            The identifier tag for the fragment
    	 * @param clz
    	 *            The fragment's Class, used to instantiate the fragment
    	 */
    	public TabListener(Activity activity, String tag, Class<T> clz) {
    	    mActivity = activity;
    	    mTag = tag;
    	    mClass = clz;
    	}
    
    	@Override
    	public void onTabReselected(Tab tab, FragmentTransaction ft) {
    	    // User selected the already selected tab. Usually do nothing.
    	}
    
    	@Override
    	public void onTabSelected(Tab tab, FragmentTransaction ft) {
    	    // Check if the fragment is already initialized
    	    Iterator<WeakReference<Fragment>> it = fragList.iterator();
    	    while (it.hasNext()) {
    		Fragment f = it.next().get();
    		if (f.getTag().equals(mTag)) {
    
    		    mFragment = f;
    		} else {
    		    ft.detach(f);
    		}
    	    }
    
    	    if (mFragment == null) {
    		// If not, instantiate and add it to the activity
    		mFragment = Fragment.instantiate(mActivity, mClass.getName());
    		ft.add(android.R.id.content, mFragment, mTag);
    	    } else {
    		// If it exists, simply attach it in order to show it
    		ft.attach(mFragment);
    	    }
    	}
    
    	@Override
    	public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    	    if (mFragment != null) {
    		// Detach the fragment, because another one is being attached
    		ft.detach(mFragment);
    	    }
    	}
    
        }
    
        List<WeakReference<Fragment>> fragList = new ArrayList<WeakReference<Fragment>>();
    
        @Override
        public void onAttachFragment(Fragment fragment) {
    	fragList.add(new WeakReference<Fragment>(fragment));
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    
    	ActionBar actionBar = getActionBar();
    
    	actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    
    	String label1 = getResources().getString(R.string.label1);
    	Tab tab = actionBar.newTab();
    	tab.setText(label1);
    
    	TabListener<Tab1Fragment> tl = new TabListener<Tab1Fragment>(this,
    		label1, Tab1Fragment.class);
    	tab.setTabListener(tl);
    	actionBar.addTab(tab);
    
    	String label2 = getResources().getString(R.string.label2);
    	tab = actionBar.newTab();
    	tab.setText(label2);
    
    	TabListener<Tab2Fragment> tl2 = new TabListener<Tab2Fragment>(this,
    		label2, Tab2Fragment.class);
    	tab.setTabListener(tl2);
    	actionBar.addTab(tab);
    
        }
    
        public void replaceFragment(View v) {
    
    	String label1;
    	FragmentManager fm;
    	FragmentTransaction ft;
    	Iterator<WeakReference<Fragment>> it;
    	int id = v.getId();
    
    	switch (id) {
    	case R.id.btn1:
    	    label1 = getResources().getString(R.string.label1);
    	    Fragment f3 = new Tab3Fragment();
    
    	    fm = getFragmentManager();
    	    ft = fm.beginTransaction();
    	    ft.replace(android.R.id.content, f3, label1);
    	    ft.commit();
    
    	    it = fragList.iterator();
    	    while (it.hasNext()) {
    		WeakReference<Fragment> ref = it.next();
    		Fragment f = ref.get();
    		if (f instanceof Tab1Fragment) {
    		    it.remove();
    		}
    	    }
    
    	    break;
    	case R.id.btn3:
    	    label1 = getResources().getString(R.string.label1);
    	    Fragment f1 = new Tab1Fragment();
    
    	    fm = getFragmentManager();
    	    ft = fm.beginTransaction();
    	    ft.replace(android.R.id.content, f1, label1);
    	    ft.commit();
    
    	    it = fragList.iterator();
    	    while (it.hasNext()) {
    		WeakReference<Fragment> ref = it.next();
    		Fragment f = ref.get();
    		if (f instanceof Tab3Fragment) {
    		    it.remove();
    		}
    	    }
    
    	    break;
    	default:
    	    break;
    	}
    
        }
    }
    
  6. launch the app
    tab1_fragment3
    tab1_fragment1
    tab2_fragment2

How it works
The method onAttachFragment is overridden, called when a fragment is attached to the activity, here I add the new fragment to the variable fragList.
The most important part of the work is carried out by the method replaceFragment() of the class TabActionBarActivity, called when the user pushes the buttons because of the attribute in tab1.xml and tab3.xml android:onClick=”replaceFragment”.
The code between the lines 126 and 143 is executed when in the first fragment the button is pushed in order to open the third one.
This code can be divided into two parts:

  1. the first part between the lines 129 and 132 replaces the current fragment (the first one) with the third fragment (beginTransaction, replace and commit); note that the fragment three is added with the tag label1=”one” and not “three”, because it is inserted in the first tab and you use the tag to understand in which tab the fragment is attached
  2. the second part between the lines 134 and 141 removes the replaced fragment from the variable fragList that contains all the added fragments

A similar code is inserted into the case for the button in the third fragment.
In the method onTabSelected of the inner class TabListener mFragment is equal to f if mTag is the right one, i.e., in the case of mTag=”one” (the first tab) fragList could contain or the fragment one or the fragment three, then mFragment is equal to one of these two fragments.

Downlaod the project here.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.