Sunday, May 8, 2011

Context Menu | Android Developer Tutorial


This is a follow up to the Options Menu Tutorial shared earlier.

To recap, the Context Menu is a floating list of menu items that appears when a user touches and holds a particular item displayed in the view, which has a menu associated with it.

Going straight to the example, first I create a ListViewwith names of pens displayed. When one presses and holds one of the names for a long time, the context menu appears as shown here:

And when you click on any of the context menu shown above, the screen that appears is:

Let’s go to the code.

First, the mundane step of creating a Listview(you can see the ListView tutorial for more explanation on this).  
I create a class ShowContextMenuextending the ListActivity. In its OnCreate(…) method, I associate the Listview array with the ListAdapater as shown here:

public class ShowContextMenu extends ListActivity {
     

    /** Called when the activity is first created. */
    @Override
    public voidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setListAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.names)));

…       
    }

Note that instead of hard-coding the list items as string array within the class, I have followed the best practice of externalizing the strings into a strings.xml resource class. Hence I use getResources().getStringArray(R.array.names) to retrieve the array of pen names that I want to display in the List.  The strings.xml file in the value folder has this entry:

<string-array name="names">
      <item>MONT Blanc</item>
      <item>Gucci</item>
      <item>Parker</item>
      <item>Sailor</item>
      <item>Porsche Design</item>
      <item>Rotring</item>
      <item>Sheaffer</item>
      <item>Waterman</item>
</string-array>

Once this Listview has been created, now we want to associate a ContextMenu with each of the rows in the Listview Item. i.e. is a user were to long-press one of the items, a menu should appear. For this we add the following line as well in the onCreate(..) method.
registerForContextMenu(getListView());

But how do we create the ContextMenu? Whenever the long-press happens, the onCreateContextMenu(…) method is invoked. So, we need to override this method as shown below:

    public voidonCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
      super.onCreateContextMenu(menu, v, menuInfo);
      MenuInflater inflater = getMenuInflater();
      inflater.inflate(R.menu.context_menu, menu);
    }

Here again, just as in Options Menu tutorial, I use a MenuInflater to create the context menu rather than do it programmatically. This is certainly a best practice of keeping the concerns separated. The View and the programming logic are kept separate as meant to be in Android Programming.  The context menu consists of 4 items – Edit, Save, Delete, View. So, here is how it is defined in the context_menu.xml in the res/menu folder:

<menu
  xmlns:android="http://schemas.android.com/apk/res/android">
      <item android:id="@+id/edit"
              android:title="@string/edit" />
      <item android:id="@+id/save"
            android:title="@string/save" />
      <item android:id="@+id/delete"
            android:title="@string/delete" />
      <item android:id="@+id/view"
            android:title="@string/view" />
</menu>

I have an id that is associated with each of the menu items that uniquely identifies the menu item selected. And I have a String associated with it which is what is displayed on the Menu.

Now that the context menu is created, how to handle when the menu item is clicked? For this we need to override the onContextItemSelected(…) method as shown below;

    public boolean onContextItemSelected(MenuItem item) {
      AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
      String[] names = getResources().getStringArray(R.array.names);
      switch(item.getItemId()) {
      case R.id.edit:
            Toast.makeText(this, "You have chosen the " + getResources().getString(R.string.edit) +
                        " context menu option for " + names[(int)info.id],
                        Toast.LENGTH_SHORT).show();
            return true;
      …………………..
      default:
            return super.onContextItemSelected(item);
      }

This is the callback method invoked when a context menu item is clicked. The callback get the reference to the clicked menu item as the parameter item.

I use the method item.getItemId() to retrieve the id of the item clicked. This is the same id that is defined in context_menu.xml. So, based on the id, I used a switch statement to jump to the appropriate action. Here the action is just to toast a message that tell which context menu was clicked for which ListView item. How do I retrieve the ListView item clicked?

Extra information about the menu is returned by calling item.getMenuInfo().  Info.id would be the ListView id. I use this id to retrieve the name of the pen  as in names[(int)info.id]

That is it. Do you want the complete code? Here it is.

Saturday, May 7, 2011

Options Menu | Android Developer Tutorial

Almost every application will need a menu in order to facilitate a user to perform actions on the application. In Android there are three types of menus possible.

  1. Options Menu
  2. Context Menu
  3. Sub Menu

The Options menu is the one that appears when a user touches the menu button on the mobile. This is something that is associated with an activity.  In 3.0 and later, this is available in the Action Bar itself for quick access.

In this article, I am showing how to create an Options Menu for devices having Android 2.3 or below.

The Context Menu is a floating list of menu items that appears when a user touches and holds a particular item displayed in the view, which has a menu associated with it.

The Sub Menu is a floating list of menu items that appears when the user touches a menu item that contains a nested menu.

There are two ways of creating an Options Menu in your application. One is by instantiating the Menu class and the other is by inflating a Menu from an XML menu resource.  Based on best practices it is always better to define the Menu in an XML and inflate it in your code.

Now, let us start with the example.

I am going to just define 3 menu items in the XML. Inflate it in my code. And when a user clicks on any of the menu items, I just Toast a message on what has been clicked.

NOTE: This is as usual not a practically useful piece, but sticking to my style, I want to keep it as uncluttered and as simple as possible so that the learning happens easily. And the focus is only on what concept we are trying to learn.

So, here is my options_menu.xml that is created in the res/menu folder:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
      <item android:id="@+id/next"
              android:icon="@drawable/ic_next"
              android:title="@string/next" />
      <item android:id="@+id/previous"
            android:icon="@drawable/ic_previous"
            android:title="@string/previous" />
      <item android:id="@+id/list"
            android:icon="@drawable/ic_list"
            android:title="@string/list" /> 
</menu>

You see that the Menu root node consists of 3 item leaf nodes. Each of the items consists of an idicon and title. The resource id is unique to that item and it allows the application to recognize which item has been clicked by the user. The icon is a drawable that should exist in the res/drawable folder and is the one shown in the menu item. The string is the item’s title.

The above assumes that you have three images ic_next, ic_previous and ic_list copied into the drawable folder. It goes without saying that these image sizes should be kept as small as possible.
Once this is ready, we will create a class called ViewOptionsMenu. It’s onCreate(…) method would be a simple one calling the super method and displaying the content as shown below.

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

The main.xml just shows the message: “Click on the Options Menu to view the available Menu Options”.  This message as per the norm is defined in the strings.xml file that exists in the res/values folder. Here are the contents of the main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/welcome"
    android:textSize="20sp" android:textStyle="bold" android:capitalize="none" android:typeface="sans"/>
</LinearLayout>

Now, I need to override the method:  onCreateOptionsMenu(Menu menu). This method is called by Android the first time the activity is loaded. This is so for Android 2.3 and below.  Here is the code:

    public boolean onCreateOptionsMenu(Menu menu) {
      MenuInflater inflater = getMenuInflater();
      inflater.inflate(R.menu.options_menu, menu);
      return true;
    }

This method is getting a handle to the MenuInflater and using it to inflate the options menu that we have defined earlier in options_menu.xml in the res/menu folder.  That is it. The Menu is created. Isn’t is so simple?

Now, that the menu is created, how do we respond to the user when he clicks on the menu. This is done by overriding the onOptionsItemSelected(MenuItem item) method in the Activity itself as shown below:

public boolean onOptionsItemSelected(MenuItem item) {
      switch (item.getItemId()) {
      case R.id.next:
            Toast.makeText(this"You have chosen the " + getResources().getString(R.string.next) + " menu option",
                        Toast.LENGTH_SHORT).show();
            return true;
      
      default:
            return super.onOptionsItemSelected(item);
      }

Here, the Android system calls this method by passing a handle to the MenuItem that has been clicked by the user. So, within this method, I check which item has been clicked by retrieving the item id through item.getItemId().  Then I use the switch statement to take action based on the id. If it is R.id.next that has been selected, then we toast a message that the “Next” menu option has been selected. In real apps, it is here that you can call the appropriate method that should take action according to the menu clicked.

Similarly I toast messages for “previous” as well as “list” menus. The complete code is downloadable here.

See the images below for how the options menu appears
 and what happens when you click on the options menu:
The whole of options menu works slightly differently for Android 3.0 and above.

And if you want to change the menu options, at runtime, you must override the onPrepareMenuOptions() method. This is called by Android each time the user clicks on the menu. This is useful for enabling, disabling, adding or removing menu items based on the current state of your application. 


Monday, April 18, 2011

Android Developers Blog: Can I use this Intent?

We all know that the loose coupling provided by Android through intents is one of its most powerful features. It makes it possible for us to mix and match the use of activities between various applications as though they all belong to one.

However, when I want to invoke another application that has published an intent filter, I cannot always assume that the other application is available on the phone. So, I need to check its availability and then only enable the feature to call it.

This is very well explained in the link: Android Developers Blog: Can I use this Intent?

Friday, March 11, 2011

TabLayout or Tabbed View | Android Developer Tutorial


This tutorial is about developing a TabLayout  / tabs which is one of the very important ways of providing the UI in Android.  The default Contacts application uses this layout.

For creating a tab Layout, we need to look at 3 new aspects: TabActivity, TabHostand TabWidget.

Let us begin by looking at how we should declare the layout xml.  The root node has to be a TabHost. What is a TabHostand why is it required? It is nothing but a container for the tabbed view we want to create. It provides methods to add the tabs, remove them and to bring focus to a specific tab etc.

Within this, we need to have a two objects: TabWidgetand a FrameLayout.

The TabWidgetitself does not do much except contain a list of tab labels that exist within the parent TabHost.  Basically it is nothing but a set of labels that the user clicks in order to select a specific tab. The actual content of each tab is to be held by the 2nd object in the TabHosti.e. a FrameLayout. Hence both the TabWidgetand FrameLayoutneed to be present on each tab. And in order to align htme one after another, we embed them into a LinearLayout.

Hence the layout XML would be like this:

<TabHost
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@android:id/tabhost"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      >
<LinearLayout
      android:id="@+id/LinearLayout01"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:orientation="vertical">
<TabWidget
      android:id="@android:id/tabs"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"></TabWidget>
<FrameLayout
      android:id="@android:id/tabcontent"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"></FrameLayout>

</LinearLayout>
</TabHost>

Now, we will move on to the coding of activities for a Tabbed Layout.

In the application, I want to have 3 tabs. So how do I do it?

Each of the tabs can have either a View, launch an activity by passing an intent or create a view dynamically by using TabHost.TabContentFactory. I have chosen to show only activities in all my tabs.

So, how many activities do you think we need? 3? Sorry 4! There needs to be a main or the parent activity that would display the whole tabbed view and one activity in each of the tabs.

Now let us look at developing the main activity. This has to extend the TabActivity.  This is no different from an Activity except for the additional ability to handle multiple embedded activities or views.

So here is the main class declaration:

public class TabLayoutSample extends TabActivity {

Let us see now, how to create the first tab.

First, we need to get a handle to the TabHostthat we have declared in the XML layout file, so that we can add tab activities into it. Here is the code for the same:

      TabHost tabHost = getTabHost();  // The activity TabHost

Into this TabHost, we need to add the tab. How can we? TabHostprovides an inner class called TabSpecthat allows you to create a tab, populate its contents and add it as a tab to the TabHost.  See how it is done:

TabHost.TabSpec spec; 

// Create an Intent to launch an Activity for the tab (to be reused)
      intent = new Intent().setClass(this, WelcomeActivity.class);

      // Initialize a TabSpec for each tab and add it to the TabHost
      spec = tabHost.newTabSpec("welcome").setIndicator("Welcome",
                          res.getDrawable(R.drawable.tab_welcome))
                      .setContent(intent);
      tabHost.addTab(spec);

In the first line above, we are creating a spec variable of type TabSpec. Then we are creating an intent that would be forming the content of the first tab. Then, we associate it with the TabSpec.

Since, each tab should have a tab indicator, content and a tag to keep track of it, it is achieved through setIndicator(..) method. Even the image that should be shown in the tab at the top is set through the same method’s 2nd parameter res.getDrawable(R.Drawable.tab_welcome). What is this and how did I set the image?

This part is a long set of steps but very easy to do. Create two images one in light color and one in dark color. Call them as welcome.png and welcome_sepia.png. Copy them into the /res/drawable-mdpi folder.  The, you need to create a state-list drawable that specifies which image to use for each tab state:

[A StateListDrawable is a drawable object defined in XML that uses a several different images to represent the same graphic, depending on the state of the object. For example, a Button widget can exist in one of several different states (pressed, focused, or neither) and, using a state list drawable, you can provide a different background image for each state.]

So, here is the tab_welcome.xml:

<?xml version="1.0"encoding="UTF-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- When selected, use grey -->
    <item android:drawable="@drawable/welcome"
          android:state_selected="true"/>
    <!-- When not selected, use white-->
    <item android:drawable="@drawable/welcome_sepia"/>
</selector>

This XML is describing which welcome image to show when the tab is selected and which to show when the tab is not selected.  This XML too needs to be created in the same folder - /res/drawable-mdpi.

Once all of this is done, when setIndicator("Welcome",                     res.getDrawable(R.drawable.tab_welcome))is called, the image associated with the tab is shown correctly based on the state.

Then, the last part of the tabSpec call is associating the activity that should be shown in the content of the activity. For this, I have created a new Intent which invokes an Activity – ‘WelcomeActivity.class’.  this implies that the WelcomeActivity.class is already created and available. In this case, it just contains a welcome message to be shown.

Once such a spec is created, I add it to the TabHostthrough the addtab(…) method of the TabHost.
I have repeated the above to show existing “contacts” in the next tab and then the “top links” in the third tab. You can get the complete code for the same here.