MenuItem Demystified

Posted by jiGGaK on May 7th, 2008

I’ve always been a bit confused as to what exactly the parameters to the MenuItem constructor do. Common practise for me, and others I assume, is to simply ignore the ordinal and priority parameters and override the Screen.makeMenu method to explicitly define order.

While this is effective, it has a few caveats:

  • makeMenu gets messy
  • separators must be added explicitly
  • doesn’t play nice with context menu items added by framework

Well what does the API say?

The obvious first place to look is the API documentation right? OK, lets have a look at the class description and constructor parameters.

Each item has an ordinal specifying the sort order within the menu, and a priority specifying which one should be the default. A lower value indicates a higher priority.

  • ordinal - Ordering parameter, lower values are placed closer to the top of the menu screen.
  • priority - The priority of the menu item. A lower value indicates a higher priority, conversely a higher value indicates a lower priority.

Ordinal seems pretty straight forward. But what relation (if any) does this have to separator placement? And what if I want my menu items be placed below the context menu items of the fields on the screen? What should my ordinal value be then?

The priority from what I can tell just determines the likelihood the menu item will be picked as the default. Lower values indication higher priority and higher values a lower priority.

Verdict

Here’s a summary first, because the details are rather dry (skip them if you like):

  • ordinal values < 10,000 are placed above context items
  • ordinal values >= to 300,000 are placed below context items
  • ordinal values with a difference larger than 100,000 have separators between them
  • use priority values >= 1000 play nice with context item priorities

Using a basic sample containing a couple fields and a prefab save menu item, I printed the ordinal ordinal and priority of each menu item in the menu generated by the MainScreen class (see source code at the end of the article).

Field context menu items have an ordinal somewhere in the neighbourhood of 10,000 to 200,000. The prefab menu item for save has an ordinal around 300,000. And based on the ordinal from the save menu item, it’s probably safe to assume that to place your menu items below context items, 300,000 is the magic number.

To have separators automagically placed between menu items, simply use an ordinal value at least 100,000 greater than the previous menu item.

Context menu items seem to have a priority value in the range of 100 to 500. The prefab menu item has a priority of 1000 so I’m guessing this is an appropriate value for most menu items.

MainScreen - Cause I’m Lazy

The MainScreen class has a method addMenuItem. The documentation for this method reads:

A main screen brings up a menu by default when Screen.onMenu(int) is invoked. If the makeMenu(net.rim.device.api.ui.component.Menu, int) method is not overridden, it will add these menu items to the menu. The close menu item is added by default.

So as long as the ordinal and priority values are set appropriately, you don’t need to bother with the makeMenu method override at all! Right?

Well there is one gotcha; MainScreen adds all all of the menu items to the menu. If you need to omit items from the menu resulting from the trackball press, then you will have to add these items exclusively in the makeMenu method.

public MyScreen() {
   addMenuItem(_saveMenuItem);
   // makeMenu will handle adding this now
   //addMenuItem(_deleteMenuItem);
}

protected void makeMenu(Menu menu, int instance) {
   super.makeMenu(menu, instance);

   // add delete menu item when NOT making context menu (trackball-click)
   if ( instance != Menu.INSTANCE_CONTEXT ) {
      menu.add(_deleteMenuItem);
   }
}

Sample Code

import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.ObjectChoiceField;
import net.rim.device.api.ui.container.MainScreen;

public class Hello extends UiApplication {
   public Hello() {
      MainScreen screen = new MainScreen(MainScreen.DEFAULT_MENU) {
         protected void makeMenu(Menu menu, int instance) {
            super.makeMenu(menu, instance);

            for (int i=0, len=menu.getSize(); i<len; i++) {
               MenuItem item = menu.getItem(i);
               System.out.println("DEBUG " + item.getOrdinal() +
                     " " + item.getPriority() + " " + item);
            }
         }
      };

      screen.addMenuItem(MenuItem.getPrefab(MenuItem.SAVE_CLOSE));

      screen.add(new EditField("Edit: ", ""));
      screen.add(new ObjectChoiceField("Choice: ", new Object[] {"1", "2"}));

      pushScreen(screen);
   }

   public static void main(String[] args) {
      new Hello().enterEventDispatcher();
   }
}


Saravanan December 1st, 2009

Thanks for your tips. It is very usefull for my applications.

Julio October 20th, 2009

Thanks for the info, clean, short and neat ;)

Kyle October 4th, 2009

Hey thanks for this… very well written and easy to understand :)