Posts Tagged ‘UI’

Blackberry UI, What’s on the Menu

Creative Commons Licence
This work is licenced under a Creative Commons Licence.

Often one of the first things we want to do is add a menu to our Screen. The MainScreen we learned of last time makes this quite easy but also flexible. First as always we need to import the new Classes we are going to use:

import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.ContextMenu;
import net.rim.device.api.ui.component.Menu;

To post a menu we over ride the base Class (MainScreen in this case) implementation of makeMenu(). Since, for the moment at least, we are only providing a single menu we can ignore instance.

First we find out which Field, if any, has focus. We can then get the contextMenu for that Field, if any, and include it in our menu. The context menu contains items like Copy and Paste or other actions that make sense in the context of the Field with focus.

In many programs and tutorials you will find line 47 written using the UiApplication Class:

        Field focus = UiApplication.getUiApplication().getActiveScreen().getLeafFieldWithFocus();

This is of course perfectly good coding, arguable better than what I’ve done here. The way I’ve coded it demonstrates that our main Class BasicGUI is a sub-Class of UiApplication so we can use it anyplace we would use UiApplication. This saves us from having to import the definition of UiApplication into the BasicGUIScreen file because the definition of BasicGUI is autmatically shared with all classes in the package src.lessonOn. So why is using UiApplication better? Well, if we decide we want to use BasicGUIScreen in an application with a different main Class that isn’t named BasicGUI, we will have a problem. Something to keep in mind.

With the potential context menu taken care of we can add our own MenuItem screen2 which we will define in a moment. Finally if we want to include the default menu items, Close and Switch Application we simply call the Super Class implementation of makeMenu(). If we leave that call out, then we can eliminate those options from the menu, if we so chose.

    protected void makeMenu(Menu menu, int instance) {

        Field focus = BasicGUI.getUiApplication().getActiveScreen().getLeafFieldWithFocus();
        if (focus != null) {
            ContextMenu context = focus.getContextMenu();
            if (!context.isEmpty()) {
                menu.add(context);
                menu.addSeparator();
            }
        }

        menu.add(screen2);

        super.makeMenu(menu, instance);
    }

One way to create a MenuItem is as an instance variable. This is no different than declaring and initializing a simple type, like an int, but a bit more complex because a MenuItem is a complex type. The three arguments to the MenuItem constructor are: the text to display in the menu; the ordinal, which is used to sort the MenuItems on the menu; and the priority which is used to dynamically select the MenuItem that is selected when the menu is first posted. When the MenuItem is selected, the run() method is executed on the Event Thread. So you must remember the priviledges and restrictions that go along with running on the Event Thread: no long processing, but you may alter the UI. In this case we create an instance of MainScreen, set the title and add a LabelField, and finally push the Screen onto the screen stack.

    private MenuItem screen2 = new MenuItem("Screen 2", 200, 200) {
        public void run() {
            MainScreen s2 = new MainScreen();
            s2.setTitle("Screen 2");
            s2.add(new LabelField("This is screen number 2"));
            BasicGUI.getUiApplication().pushScreen(s2);
        }
    };

And here is the new BasicGUIScreen.java source file:

/*
 * BasicGUIScreen.java
 *
 * © Richard Buckley www.hrbuckley.net, 2010
 *
 * This work is licenced under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
 * To view a copy of this licence, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
 * Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
 */

package src.lessonOne;

import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.ContextMenu;
import net.rim.device.api.ui.component.Menu;

//
//
//
public class BasicGUIScreen extends MainScreen {
    BasicGUIScreen() {
        setTitle("Hello World");
        add(new LabelField("Hello World"));
    }

    private MenuItem screen2 = new MenuItem("Screen 2", 200, 200) {
        public void run() {
            MainScreen s2 = new MainScreen();
            s2.setTitle("Screen 2");
            s2.add(new LabelField("This is screen number 2"));
            BasicGUI.getUiApplication().pushScreen(s2);
        }
    };

    //
    // Populate the screen menu
    // @param menu the menu to populate
    // @param instance the instance, allows different menus to be posted depending
    // on user interaction. It will be 0 if the menu is posted by pressing the BB
    // button, non-zero if it was posted by a trackball click.
    //
    protected void makeMenu(Menu menu, int instance) {

        Field focus = BasicGUI.getUiApplication().getActiveScreen().getLeafFieldWithFocus();
        if (focus != null) {
            ContextMenu context = focus.getContextMenu();
            if (!context.isEmpty()) {
                menu.add(context);
                menu.addSeparator();
            }
        }

        menu.add(screen2);

        super.makeMenu(menu, instance);
    }
}

We could have created this screen in a number of ways. The most flexible would have been to create a new sub-Class of MainScreen. We could also reuse BasicGUIScreen. Similarly the MenuItem could be created when makeMenu is run if and when it is needed rather that when BasicGUIScreen is instantiated as screen2 is:

        menu.add(new MenuItem("Screen 3", 200, 300) {
            public void run() {
                BasicGUIScreen bgs = new BasicGUIScreen();
                bgs.setTitle("Screen 3");
                BasicGUI.getUiApplication().pushScreen(bgs);
            }
        });

Java allows all kinds of shortcuts like this. Sometimes they are very handy and improve readability, sometimes not.

As always, the Blackberry JDE project files are available from the Lesson1 Git repository. Enjoy.

Blackberry UI: Events, Screens and Threads

Creative Commons Licence
This work is licenced under a Creative Commons Licence.

There are several things that you must always bear in mind when writting BlackBerry applications: the device is event driven on a single Java Virtual Machine (JVM); the user interface is implemented using screens which are held on a screen stack; and the OS is multi-threaded, some threads have special requirements and capabilities which must be respected.

Driven by Events

Event driven software is not a new concept, especially where graphical user interfaces (GUIs) are concerned. When the user interacts with part of the interface, that interaction is presented to the appropriate software as an event. As programmers our job becomes writing little code fragments that are all related but each one takes care of some element of the user interface. This is a very powerful concept and has allowed much of what we take for granted on our digital devices to be developed. It does mean that we are no longer able to say ahead of time which order our bits of code will be executed in. That power is given over to the user.

On the BlackBerry platform, events are delivered to Listeners. A Listener is simply an object that implements a particular interface, a method or group of methods that provide the object with a specified behaviour. We don’t need to concern ourselves too much with the details of how the event delivery mechanism works, but we do need to keep certain things in mind. The operating system collects the very low level events: key presses and releases, track ball movement, data input/output etc. and delivers them to applications by placing the events in an event queue. When an application gets a turn to execute, a waiting event is taken off the queue and delivered to any listeners registered for that event. The listener may be provided by the OS or the API and it may combine many low level events into one or more higher level events. For example track ball movement and clicks may be combined into a drop down list item selection. These higher level events are then dilivered to their listeners in turn. All this event delivery is done by the Event Thread, and there is only one Event Thread. So if your program receives an event and starts a process that will take a long time (like computing Pi to thousands of decimal places) or that may block waiting for something external to the device (like downloading a web page) no other applications will receive events while your greedy program hogs the Event Thread. This will cause the device to appear to freeze. If this goes on too long, the event queue will fill up and the OS will terminate your program. So, keep your listeners short and quick. If you need to do long computation or IO start a new thread to handle those tasks.

The Event Thread, or more precicely the thread that is holding the user interface event lock is the only thread that can manipulate user interface elements. There are ways of arranging for your code to be executed on the Event Thread, or to grab the event lock, so that non-Event Threads can still have their results displayed to the user. We will look at those a little later.

Stack those Screens

Within a UiApplication Screens are held on a stack. The topmost screen is displayed to the user and receives events directed to the application. To display a Screen an Object that is a sub-class of net.rim.device.api.ui.Screen is created and pushed onto the Screen stack with pushScreen() (or one of its variants). To un-display a Screen, it can be popped off the stack manually with popScreen(), or it can be closed by calling the Screen’s close() method. This is what happens to some Screen implementations (like the MainScreen) when the user presses the escape key. When the last Screen is popped off the Screen stack, the application will exit, though it is possable to change this behaviour to put the application in the background.

Unraveling Threads

We have alread discussed threads in enough detail for the moment when we discussed Events. So lets get on with programming.

Adding a MainScreen

There are many ways to add a new Class or Object to an existing project. We will be talking about each when we need to use it, for now let’s use the traditional way of putting each Class in its own file so we create a BasicGUIScreen.java file and start typing. We will put it in the same package as our main object:

package src.lessonOne;

We also need to import the classes we will be using:

import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;

And we can provide the universal program greeting:

public class BasicGUIScreen extends MainScreen {
    BasicGUIScreen() {
        setTitle("Hello World");
        add(new LabelField("Hello World"));
    }
}

To have our Screen displayed we simply modify the constructor of our main Class to create an instance of our Screen and push it onto the stack. We now have a program that does something we can see. Sitll not particularly useful, but it is a begining we can build on.

    BasicGUI() {
        pushScreen(new BasicGUIScreen());
    }

Here are the complete files, first BasicGUI.java:

/*
 * BasicGUI.java
 *
 * © Richard Buckley www.hrbuckley.net, 2010
 *
 * This work is licenced under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
 * To view a copy of this licence, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
 * Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
 */

package src.lessonOne;

import net.rim.device.api.ui.UiApplication;

/**
 *
 */
public class BasicGUI extends UiApplication {
    public static void main(String[] args) {
        BasicGUI basicGUI = new BasicGUI();
        basicGUI.enterEventDispatcher();
    }

    BasicGUI() {
        pushScreen(new BasicGUIScreen());
    }
}

And BasicGUIScreen.java:

/*
 * BasicGUIScreen.java
 *
 * © Richard Buckley www.hrbuckley.net, 2010
 *
 * This work is licenced under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
 * To view a copy of this licence, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
 * Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
 */

package src.lessonOne;

import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;

/**
 *
 */
public class BasicGUIScreen extends MainScreen {
    BasicGUIScreen() {
        setTitle("Hello World");
        add(new LabelField("Hello World"));
    }
}
BasicGUI running on the simulator

BasicGUI running on the simulator

Visitors

Site Links

  • Blackberry Forums
  • Blackberry Support Forums
  • FreshMeat
  • Radio Electronics
  • SlashDot
  • Smiths Falls Weather
  • Stack Overflow
  • XKCD
  • Yahoo Groups

Blogroll

Meta