High Level User Interface

An Introduction to the High-Level User Interface APIs: Alerts & Tickers.

The Mobile Information Device Profile (MIDP) includes both a low-level user interface (UI) API and a high-level UI API. The low-level API gives you complete access to a device's screen and to raw key and pointer events. However, no user interface controls are available with the low-level API -- your application must explicitly draw buttons and other familiar controls. This is the price you pay for the flexibility of the low-level API.

The situation is reversed with the high-level API. It provides simple user interface controls, but no direct access to the screen or to raw input events. The controls are fairly abstract to account for the differences in screen size and input methods between various MIDP devices. The MIDP implementation decides how to draw the control and how to manage user input.

You can use both low-level and high-level APIs in a single MIDlet, just not at the same time. For example, games that rely on the low-level API to control the screen can also use the high-level API for online help or to display high scores. Business applications that use the high-level API for UI controls can also use the low-level API to draw graphs.

For more information about the low-level UI API, see the J2ME Tech Tip for March 19, 2001, Using the MIDP Low-Level User Interface APIs.

The following tip introduces the high-level UI API, and uses two basic controls to do that: alerts and tickers.

An alert is basically a message dialog, that is, a way of presenting a single string to the user (along with an optional image or sound). Alerts display warnings, errors, alarms, notices, or confirmations. Your application's user interface is disabled while an alert is displayed. The alert then either times out automatically (the timeout value is programmable) or remains on screen until the user explicitly dismisses it.

You display an alert by creating an instance of the javax.microedition.lcdui.Alert class. You specify a title (which can be null) as a parameter to the constructor. A number of setter methods are available to define the alert's properties. For example:


import javax.microedition.lcdui.*;

Image icon = ...; // code omitted

Alert msg = new Alert( "Error!" );
msg.setImage( icon );
msg.setString( "No connection was possible." );
msg.setTimeout( 5000 ); // in milliseconds
msg.setType( AlertType.ERROR );

You don't need to set all properties. For example, the image is optional, and can be null. (Even if you set the image, the device might not be able to display it.) However, at a minimum, you should always define a title and a message string. As far as other properties:

You can also specify all properties, except for the timeout value, in the constructor. For example:


Alert msg = new Alert( "Error!",
                       "No connection was possible.",
                       icon,
                       AlertType.ERROR );
msg.setTimeout( Alert.FOREVER ); // make it modal

After you've defined the alert, you display it by calling Display.setCurrent. You pass the method both a reference to the alert and a reference to a displayable element. A displayable element is a top-level user interface object that extends the Displayable class -- your application will have at least one of these whether it's using the low-level or high-level UI APIs. The system immediately activates the displayable element when the alert times out or is dismissed. To return to the displayable element that was active just before the alert was displayed, simply do the following:


Display display = ....; // assign on startup
Alert   msg = ....; // create an alert

display.setCurrent( alert, display.getCurrent() );

Notice that the setCurrent method is invoked on an instance of the Display class. You can obtain the class when the MIDlet starts by calling the static Display.getDisplay method, passing the MIDlet instance as its only parameter. For example:


public MyApp extends MIDlet
{
    private Display display;
      
    public MyApp(){
        display = Display.getDisplay( this );
    }
  
    public Display getDisplay(){
        return display;
    }
   
    // rest of MIDlet goes here
}

It's simplest to save the Display instance as a member of your main application class and make it available to the other classes in your application.

Note that calling Display.setCurrent simply changes what is displayed on the screen -- it doesn't halt the current thread of execution, even if the alert is modal. You typically call it in response to some event; you should exit the method you're in as soon as possible to let the system's event processing to continue unhindered.

The sound associated with an alert must be one of five predefined instances of the AlertType class: INFO, WARNING, ERROR, ALARM, or CONFIRMATION. The device associates different sounds with each class, if possible, though some devices may not even support sound.

You can play any sound at any time by calling the playSound method, as in:


    AlertType.ERROR.playSound( display );

The icon associated with an alert must be an instance of the Image class. The icon should be as small as possible and look good when mapped to monochrome.

Now what about tickers? A ticker is a user interface control that displays a single line of text, scrolling it onscreen at regular intervals just like an old-fashioned movie marquee. To use a ticker, you create an instance of the Ticker class, passing the string you want displayed into the constructor. For example:


import javax.microedition.ldcdui.*;

Ticker ticker = new Ticker( "Hello, world!" );

To display the ticker, you associate it with a top-level window created by the high-level UI, that is, any class that extends the Screen class. You associate the ticker by calling the setTicker method, as in:


Form f = new Form( "A Title" );
f.setTicker( ticker );

The ticker is displayed in an appropriate area of the window. You can share the same ticker across different windows; the system then attempts to keep it in the same position.

The only thing you can do with a ticker is change its text by calling the setString method. To stop displaying a ticker, remove it from the top-level window by passing null to the setTicker method.

Here's a simple stock tracking MIDlet that demonstrates the use of alerts and tickers. Note that the stock values are generated randomly for example purposes. In the real world, you would use the HttpConnection class to obtain stock values from a web site. Note particularly how the alert text can be changed while the alert is still active.


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class StockWatcher extends MIDlet {

    Display      display;
    Ticker       ticker = new Ticker( "" );
    Command      exitCommand = new Command( 
                           "Exit", Command.EXIT, 1 );
    Timer        timer = new Timer();
    StockChecker checker = new StockChecker();
    TickerForm   form = new TickerForm();
    Alert        alert = new Alert( "Stock Alert!" );

    public StockWatcher() {
        display = Display.getDisplay( this );
        alert.setTimeout( Alert.FOREVER );
    }

    protected void destroyApp( boolean unconditional ) {
    }

    protected void startApp() {
        display.setCurrent( form );
        timer.schedule( checker, 0, 30000 );
    }

    protected void pauseApp() {
    }

    public void exit(){
        timer.cancel();
        destroyApp( true );
        notifyDestroyed();
    }

    // Display a simple form to hold the ticker

    class TickerForm extends Form implements
                                   CommandListener {
        public TickerForm(){
            super( "Stock Watch" );
            setTicker( ticker );
            addCommand( exitCommand );
            setCommandListener( this );
        }

        public void commandAction( Command c,
                                    Displayable d ){
            exit();
        }
    }
  
    // Check the stock values and put up an alert if
    // they're beyond certain limits....

    class StockChecker extends TimerTask {
        Random       generator = new Random();
        int          sybsValue = 20000;
        int          sunwValue = 30000;
        int          ibmValue = 40000;
        StringBuffer buf = new StringBuffer();

        public void run(){
            String values = getStockValues();

            ticker.setString( values );

            if( sybsValue < 18000 || sybsvalue > 22000 ||
                sunwValue < 28000 || sunwvalue > 32000 ||
                ibmValue < 38000 || ibmvalue > 42000 ){
                alert.setString( values );
            }

            if( !alert.isShown() ){
              display.setCurrent( alert, form );
            }
        }

        private String getStockValues(){
            sybsValue = randomStockValue( sybsValue );
            sunwValue = randomStockValue( sunwValue );
            ibmValue = randomStockValue( ibmValue );

            buf.setLength( 0 );
            appendValue( "SYBS", sybsValue );
            appendValue( "SUNW", sunwValue );
            appendValue( "IBM", ibmValue );

            return buf.toString();
        }

        // Generate a random stock value... in the
        // real world you'd use HTTP to obtain the
        // stock value from a broker's website.

        private int randomStockValue( int oldVal ){
            int incr1 = ( generator.nextInt() % 2 );
            int incr2 = ( generator.nextInt() % 16 );

            if( incr1 < 1 ){
                oldval -= incr1 * 1000;
            } else {
                oldval += ( incr1 - 2 ) * 1000;
            }

            if( incr2 < 8 ){
                oldval -= incr2 * 250;
            } else {
                oldval += incr2 * 250;
            }

            return oldval;
        }

        private void appendvalue( string stock, int val ){
            buf.append( stock );
            buf.append( ' ' );
            buf.append( integer.tostring( val / 1000 ) );
            buf.append( '.' );
            buf.append( integer.tostring( val % 1000 ) );
            buf.append( ' ' );
        }
    }
}