Navigation tabs with Fragments using ActionBarSherlock

This post covers how to use navigation tabs in the ActionBar using ActionBarSherlock. Although the demo in the ActionBarSherlock download shows how to create navigation tabs, they are not associated with Fragments. I looked around online and could not find any tutorials for it. Now that I’ve figured it out,  I thought I’d share it.

This tutorial has two Fragments, FragmentA and FragmentB, each of which has a TextView and a Button in their layouts.

Here’s FragmentA.java: It implements an OnClickListener for the Button to show a Toast.


public class FragmentA extends Fragment {
Button button;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved)
{
return inflater.inflate(R.layout.frag_a, group, false);
}
@Override
public void onActivityCreated (Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
button = (Button) getActivity().findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "You clicked button on Fragment A", Toast.LENGTH_LONG).show();
}
});
}
}


Here’s FragmentB.java: It also implements the Button OnClickListener, but to display a dialog.


public class FragmentB extends Fragment {
Button button;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved)
{
return inflater.inflate(R.layout.frag_b, group, false);
}
@Override
public void onActivityCreated (Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
button = (Button) getActivity().findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Fragment B");
builder.setMessage("What would you like to do?");
builder.setPositiveButton("Nothing", null);
builder.setNegativeButton("Leave me alone!", null);
builder.show();
}
});
}
}

And finally, here’s the Activity that hosts these Fragments, FragmentDemoActivity


public class FragmentDemoActivity extends SherlockFragmentActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = bar.newTab();
ActionBar.Tab tab2 = bar.newTab();
tab1.setText("Fragment A");
tab2.setText("Fragment B");
tab1.setTabListener(new MyTabListener());
tab2.setTabListener(new MyTabListener());
bar.addTab(tab1);
bar.addTab(tab2);
}
private class MyTabListener implements ActionBar.TabListener
{
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if(tab.getPosition()==0)
{
FragmentA frag = new FragmentA();
ft.replace(android.R.id.content, frag);
}
else
{
FragmentB frag = new FragmentB();
ft.replace(android.R.id.content, frag);
}
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
}
}

Screenshots of the end result

The entire project is available as an archive here

On an unrelated note, I’ve just realised how UGLY code without syntax highlighting looks in blog posts. Any solutions?

Advertisements

31 Comments

  1. Fantastic! Thanks for sharing this, it has solved one of my problems. Excellent code, very easy to understand. I finally understand how Fragments work now (Android newbie).

    Next step, see if a fragment can hold a ListView.

    Reply

    1. Glad I could help! As for ListViews in fragments, you can use a SherlockListFragment, or a simple Fragment which has a ListView in it.

      Reply

      1. Null pointer exception at bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        so obviously getSupportActionBar rets null, but why?

        I’m an iOS wizard but Android noobie.. So frustrating!

        Reply

    1. This is not necessarily an issue with fragments/ViewPagers. It has more to do with the Activity lifecycle. Android destroys the activity and recreates a new instance of the Activity when orientation changes are made. You can fix this by saving the current index of the ViewPager when the activity is destroyed, and changing the ViewPager to that index when the activity is recreated.

      In onSaveInstanceState, save the index as follows
      bundle.putInt(“index”, pager.getCurrentItem());

      In onRestoreInstanceState, restore the index as follows
      int index = bundle.getInt(“index”);
      pager.setCurrentItem(index);

      You can find a SO answer explaining onSaveInstanceState and onRestoreInstanceState here

      Reply

  2. Its running but i cannot see anything withing the fragments as in I can only see the tabs but nothing inside the tabs i.e the respective fragments.Even when I your code !!

    Reply

  3. Hi,
    Can you do something so that an logout button will shown along the top right of the app icon and title name.Please help if you can.I am stuggling for this……..
    Please reply

    Reply

  4. The TabsPager always automatically loads the next/previous Page. I load dynamic Content via http in each Tab. Is there a way to initiate the Reuuest only when the Tab is visible?

    Reply

    1. I think you’re talking about ViewPagers. One of the main attractions of ViewPager IMO is that it pre-loads content in the fragments adjacent to the one currently being viewed. If that does not go well with your app, have you considered using ActionBar navigation tabs, as described in this post? The default dialler app in ICS and JB uses navigation tabs.

      AFAIK there is no way to prevent the ViewPager from pre-loading fragments. It can only be modified to increase the number of fragments that are pre-loaded on each side [2, 3 etc as opposed to the default: one Fragment on each side]

      Reply

      1. Yes, but using this method (replace) after switching Back to another Tab, the Tab content has been reset to default.

        So I used a solution to hide and show the fragments, but I don’t know if it’s good to keep all the Fragments hidden and in the memory.

        When detaching and Adding a Fragment, the Fragment Object doens’t get released

        Reply

        1. I haven’t tested it, but you should be able to solve the reset issue simply by creating two Fragment fields _fragA and _fragB and doing this in onTabSelected:

          if(tab.getPosition()==0)
          {
          if ( _fragA == null )
          {
          _fragA = new FragmentA();
          }
          ft.replace(android.R.id.content, _fragA);
          }
          else
          {
          if ( _fragB == null )
          {
          _fragB = new FragmentB();
          }
          ft.replace(android.R.id.content, _fragB);
          }

          This should prevent the creation of new Fragments every time the tab is changed, and also avoid the unfavourable effects of having a hidden Fragment in the UI. Let me know if this works.

          Reply

  5. Both with your code and my code, in the if statement in the onTabSelected method I get an error on ft.replace.
    “The method replace(int, Fragment) in the type FragmentTransaction is not applicable for the arguments (int, FragmentA)”

    If I choose to change the type to ‘Fragment’ I get the error “Type mismatch: cannot convert from FragmentA to Fragment”. So basically the solutions creates their respective errors, how to fix this? If I just use ‘Fragment’ for both the fragments are empty when running the app.

    Reply

    1. Be sure to import “android.support.v4.app.FragmentTransaction” instead of “android.app.FragmentTransaction” in FragmentA,FragmentB and Main classes.

      Hope it helps!

      BTW, if I run this demo code in a large screen device, like a tablet, both tabs are shown in the same ActionBar as the title, not in individual bars like in the example pictures. Is there a way to force those tabs to be shown in a different bar. Thanks in advance.

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s