Thursday, June 28, 2012

Actionbars and tabs under Android 2.x+ with ActionBarSherlock

Introduction

ActionBarSherlock (ABS) is an extension of the Android Support Library which even more simplifies the life with ActionBars and their compatibilty until Android 2.x.

Here I'll show you a short Hello World introduction to ABS. Please note that I won't go in detail about all functions, I just want to give a starting point for using ABS.

The Android Developers documentation is a very good site to learn more about ActionBars, tabs, fragments and navigation!

Creating the ABS project

  1. Download and unpack the latest version of ABS from here:  abs.io/download.html
  2. Create a new Android project:
    -  choose the library folder as the project's source
    -  the build target should be at least API level 13 (Android 3.2)
Compile the project if you haven't turned on Automatic building.

If you get tons of errors, it's probably because of the default Java compliance level. Change it in the project options under Java Compiler to at least 1.6.

Hello World, ABS!

  1. Create new project with a build target set to API level 13 or higher. Set the main activity name to MainActivity, so you can just copy and paste (but also understand better) this code.
  2. Go to the Android options under your project's options. Add ABS as a library via the Add button.

Now, we're ready for the code...

MainActivity.java
package de.tutorials.comfreek.actionbar;
 
import android.os.Bundle;
 
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
 
public class MainActivity extends SherlockFragmentActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        ActionBar actionbar = getSupportActionBar();
        actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
 
        Tab tab1 = actionbar.newTab().setText("Greeting 1");
        Tab tab2 = actionbar.newTab().setText("Greeting 2");
 
        tab1.setTabListener(new MyTabListener(this, "tab1",
                AFragment.class));
 
        tab2.setTabListener(new MyTabListener(this, "tab2",
                BFragment.class));
 
        actionbar.addTab(tab1);
        actionbar.addTab(tab2);
    }
}
The code should be mostly self-explanatory. The TabListener will receive three events from our ActionBar:

  • onTabSelected
  • onTabUnSelected
  • onTabReselected
It has to handle how the fragments (=tab content) are exchanged and created. A fragment is a part of a UI. You can also have multiple fragments in a activity so they are displayed dependent of the device's screen size. (But at the moment a fragment cannot hold another fragment, so you can only display one fragment in tab and you have to manage the exchange yourself).

main.xml

  
 
 

The inner LinearLayout will act as the fragment container for the tab content.
In addition, we have to set either the app's theme or the MainActivity's theme to @style/Theme.Sherlock.
You can either use the ADT Manifest editor for it or you can directly edit the XML:

    



    

AFragment and its layout file for the first tab content:
package de.tutorials.comfreek.actionbar;
 
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
 
import com.actionbarsherlock.app.SherlockFragment;
 
public class AFragment extends SherlockFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.hello, container, false);
    }
}

 
    
 

The code of BFragment is identical except the displayed text. Now TabListener.java which handles all tab events and their fragment exchanges:
package de.tutorials.comfreek.actionbar;
 
import android.app.Activity;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
 
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.ActionBar.TabListener;
 
public class MyTabListener implements TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class mClass;
 
    /** Constructor used each time a new tab is created. */
    public MyTabListener(Activity activity, String tag, Class clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }
 
    /* The following are each of the ActionBar.TabListener callbacks */
 
    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ignoredFt) {
        FragmentManager fragMgr = ((FragmentActivity) mActivity)
                .getSupportFragmentManager();
        FragmentTransaction ft = fragMgr.beginTransaction();
 
        // Check if the fragment is already initialized
        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);
        }
 
        ft.commit();
    }
 
    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ignoredFt) {
        FragmentManager fragMgr = ((FragmentActivity) mActivity)
                .getSupportFragmentManager();
        FragmentTransaction ft = fragMgr.beginTransaction();
 
        // Check if the fragment is already initialized
        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.detach(mFragment);
        }
 
        ft.commit();
    }
 
    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}
You're finished! Just start the app on your Android device or in the simulator:



7 comments:

  1. Could you please post the code for this. My code is not compiling.

    ReplyDelete
  2. Hi, thanks for your comment!
    What errors are being output? Here is a packed ZIP of the whole project: http://www.tutorials.de/content/attachments/60460d1342552698-tutsactionbar.zip.html

    ReplyDelete
  3. The newer versions of ABS could be incompatible, I haven't tested the code since some weeks.

    ReplyDelete
  4. Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.linearlayout" on path: /data/app/com.ABS.abstabtest-1.apk

    ReplyDelete
  5. Ok about the last comment linearlayout must be typed LinearLayout in xml files.

    ReplyDelete
  6. I've encountered a null pointer when I set the actionbar to navigation tabs "getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);". How could I resolve this issue?

    ReplyDelete
    Replies
    1. Hi,

      this tutorial could be quite a bit out-of-date due to the creation time.

      Since I've moved a bit away from Android apps (more Win8 stuff, etc.), I suggest you contacting someone at http://actionbarsherlock.com/.

      Good luck!

      Delete