Java Technology Home Page
A-Z Index

Java Developer Connection(SM)
Technical Tips

Downloads, APIs, Documentation
Java Developer Connection
Tutorials, Tech Articles, Training
Online Support
Community Discussion
News & Events from Everywhere
Products from Everywhere
How Java Technology is Used Worldwide
Print Button

Members Only Requires login

Early Access Members Only

Downloads

Bug Database Members Only
Submit a Bug
View Database

Newsletters
Back Issues
Subscribe

Learning Centers
Articles
Bookshelf
Code Samples
New to Java
Question of the Week
Quizzes
Tech Tips
Tutorials

Forums

Technology Centers
Tech Tips archive

J2ME Tech Tips

June 22, 2001

WELCOME to the Java Developer ConnectionSM (JDC) JavaTM 2 Platform, Micro Edition (J2METM) Tech Tips, for June 22, 2001. This issue covers:

The J2ME Tech Tips are written by Eric Giguere (http://www.ericgiguere.com), an engineer at iAnywhere Solutions, inc. Eric is the author of the book "Java 2 Micro Edition: Professional Developer's Guide" and co-author of the upcoming "Mobile Information Device Profile for Java 2 Micro Edition," both books in John Wiley & Sons' Professional Developer's Guide series.


USING THE MIDP LIST COMPONENT

The Mobile Information Device Profile (MIDP) groups its user interface components into two sets of classes: the high-level API and the low-level API. With the high-level API you define your user interface using a set of platform-independent abstractions and let the MIDP implementation decide the look and behavior of the user interface. The low-level API gives you complete control (and responsibility) of the drawing area and the processing of the raw input events. The low-level API was covered in the J2ME Tech Tip for March 19, 2001 "Using the MIDP Low-Level User Interface API".

This Tech Tip examines the List component, part of the high-level API. All MIDP user interface components are defined in the javax.microedition.lcdui package.

The List component displays a set of selectable strings. An optional image can also be displayed along with each string. The implementation displays the strings in some kind of list -- the exact format of the display is undefined -- and provides a way for the user to navigate from one item in the list to another, selecting or deselecting items as appropriate. The navigation and selection procedures are separate. In other words, the user can navigate to a specific item without selecting or deselecting it. This is referred to as moving the input focus.

The List class extends Screen, which is the base class for all top-level windows in the high-level API. Only one screen is ever visible at a time. A screen has an optional title. Because the Screen class extends Displayable, Command objects can also be associated with List components. The List class also implements the Choice interface, which defines a number of methods that the List component has in common with another component of the high-level API: the ChoiceGroup component.

A List component supports three modes of item selection. The first mode, List.EXCLUSIVE, lets the user select exactly one item from the list. If the user selects an item and another item is already selected, the previous item is automatically deselected. Conceptually, it's as if the list was composed of a set of radio buttons, and implementations may in fact display it that way. The second mode, List.MULTIPLE, lets the user select zero or more of the items simultaneously. It's as if the list was composed of a set of checkboxes. The final mode, List.IMPLICIT, is like the exclusive mode, but the act of selecting an item also triggers an event, as if the user had triggered a command (more on this below).

The List class defines two constructors. Both constructors take the screen title and the list mode (one of List.EXCLUSIVE, List.MULTIPLE, or List.IMPLICIT). The second constructor also takes an array of strings and an array of images in order to initialize the List. However, the initialization can be done separately. For example, here's one way to construct a list of items:

List l = new List( "Choose fruit:", List.EXCLUSIVE );
l.append( "orange", null );
l.append( "apple", null );
l.append( "pear", null );

The alternative is to use the array form of the constructor:

  
String[] fruitNames = { "orange", "apple", "pear" };

List l = new List( "Choose fruit:", List.EXCLUSIVE,
                   fruitNames, null );

Note that images are entirely optional. Even if you specify them, the implementation might ignore them. So it's safe to pass in null whenever an image or array of images is requested.

Before displaying a List component, you'll want to register a command listener for it:

List l = ....;
CommandListener listener = ....; // often "this"

l.setCommandListener( listener );

A command listener is an instance of any class that implements the CommandListener interface. Often the MIDlet's main class is the listener. Only one listener can be registered at a time. A List in EXCLUSIVE or MULTIPLE mode must also register at least one Command object, otherwise no events will be sent to the command listener. This is because the EXCLUSIVE and MULTIPLE modes do not trigger any events as the user interacts with the list.

It's usually a good idea to add commands to lists in IMPLICIT mode as well as in EXCLUSIVE or MULTIPLE mode. When the user selects an item from an IMPLICIT mode list, the list notifies its command listener using the special Command object defined as List.SELECT_COMMAND. This object gets passed to the listener's commandAction method as the first argument whenever a new item is selected. In other words, you check for implicit selection using code like this:

public void commandAction( Command c, Displayable d ){
    if( c == List.SELECT_COMMAND ){
        // implicit selection...
    } else if( ..... ){
        ..... // etc. etc.
    }
}

Make sure that any operation performed in response to SELECT_COMMAND is intuitively obvious to the user. For example, a List component that displays a list of emails might respond to SELECT_COMMAND by displaying the text of the selected email message. Other mail management operations are accessed by registering appropriate Command objects.

The List contents, that is, the text and images displayed by the List, can be changed at any time. List defines append, delete, insert, and set methods for this purpose. A common requirement, for example, is to delete the contents of a list. You can do this easily, like this:

public static void deleteListContents( List l ){
    int n = l.size();
    while( n-- > 0 ){
        l.delete( n );
    }
}

The size method returns the number of items currently stored in the List.

If you find yourself doing these kinds of operations repeatedly, you might want to extend List to augment it with your own features, as in the following:

public class ExtendedList extends List {
    public ExtendedList( String title, int mode ){
        super( title, mode );
    }
    
    public ExtendedList( String title, int mode, 
                         String[] itemText, Image[] 
				      itemImages ){
        super( title, mode, itemText, itemImages );
    }
    
    public void deleteAll(){
        int n = size();
        while( n-- > 0 ){
            delete( n );
        }
    }
} 

After a List has been constructed, you can obtain the index of the selected item at any time. In EXCLUSIVE or IMPLICIT mode, use getSelectedIndex, which returns the index (starting at 0) of the selected item:

    List l = ....; // some list
    int  which = l.getSelectedIndex();

Lists in MULTIPLE mode return -1 for getSelectedIndex, because more than one item can be selected at any given time. Use getSelectedFlags to return the selected state of each item into an array you supply:

    List      l = ....; // some list
    boolean[] selected = new boolean[ l.size() ];
    
    l.getSelectedFlags( selected );
    for( int i = 0; i < selected.length; ++i ){
        if( selected[i] ){
            System.out.println( "Item " + i + " is 
				     selected!" );
        }
    }

You can also check the selected state of an individual item at any time by calling isSelected. You can set its state by calling setSelectedFlags or setSelectedIndex. For example, here's how to toggle the selected items in a MULTIPLE mode List:

public void toggleItems( List l ){
    boolean[] selected = new boolean[ l.size() ];
    l.getSelectedFlags( selected );
    for( int i = 0; i < selected.length; ++i ){
        selected[i] = !selected[i];
    }
    l.setSelectedFlags( selected );
}

Another way to toggle the selected items is to set each item's selected state individually:

public void toggleItems( List l ){
    int n = l.size();
    for( int i = 0; i < n; ++i ){
        l.setSelectedIndex( i, !l.isSelected( i ) );
    }
}

The latter method might cause excessive repainting of the list, however, and should be avoided in favor of calling setSelectedFlags.

Each item in a List can have an associated image. An image is an instance of the Image class, usually obtained by calling Image.createImage, and passing it the path of an image in the MIDlet suite's JAR file. For example:

Image checked = null;
Image unchecked = null;

try {
    checked = Image.createImage( "/images/check.png" );
    unchecked = Image.createImage( 
                            "/images/unchecked.png" );
}
catch( java.io.IOException e ){
}

You can also create images dynamically by using the Image class's offscreen buffer capability. However, any image used in a List or other high-level UI component must be an immutable, that is, it must be an unchangeable image. (See the Image class documentation for information on how to do this with dynamically-generated images.)

Once you have an image or set of images, you can assign them to the items either in the constructor or as arguments to the append, insert or set methods. You assign the items in the constructor by passing in an array of images equal in length to the array of strings that define the items. Here's an example that illustrates assigning images to the append method:

List l = ....; // some list

try {
    l.append( "orange", Image.createImage( 
 			"/orange.png" ) );
    l.append( "apple", Image.createImage( 
			 "/apple.png" ) );
    l.append( "pear", Image.createImage( 
			  "/pear.png" ) );
}
catch( IOException e ){
}

Keep the images as small as possible, no more than 10-to-16 pixels high. Make them all the same size to ensure that the text is painted correctly. Don't depend on the images being there, however, and make sure the item text is descriptive enough without the image.

Let's end the tip with a simple MIDlet that demonstrates the use of the List component.

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

public class ListDemo extends MIDlet {

    private Display              display;
    private int                  mode = List.IMPLICIT;

    private Command exitCommand = new Command( "Exit",
                                   Command.SCREEN, 2 );
    private Command selectCommand = new Command( "Select",
                                       Command.OK, 1 );
    private Command nextCommand = new Command( "Next",
                                   Command.SCREEN, 2 );
    public ListDemo(){
    }

    protected void destroyApp( boolean unconditional )
                   throws MIDletStateChangeException {
        exitMIDlet();
    }

    protected void pauseApp(){
    }

    protected void startApp() throws 
    MIDletStateChangeException {
        if( display == null ){ // first time called...
            initMIDlet();
        }
    }

    private void initMIDlet(){
        display = Display.getDisplay( this );
        display.setCurrent( new SampleList( mode ) );
    }

    public void exitMIDlet(){
        notifyDestroyed();
    }

    public static final String[] items = {
        "First", "Second", "Third", "Fourth"
    };

    class SampleList extends List implements 
                           CommandListener {

        private int mode;

        SampleList( int mode ){
            super( "", mode, items, null );
            addCommand( exitCommand );
            addCommand( selectCommand );
            addCommand( nextCommand );
            setCommandListener( this );

            switch( mode ){
                case IMPLICIT:
                    setTitle( "Implicit" );
                    break;
                case EXCLUSIVE:
                    setTitle( "Exclusive" );
                    break;
                case MULTIPLE:
                    setTitle( "Multiple" );
                    break;
            }

            this.mode = mode;
        }

        public void commandAction( Command c, 
                             Displayable d ){
            if( c == exitCommand ){
                exitMIDlet();
            } else if( c == selectCommand ){
                showSelection( false );
            } else if( c == SELECT_COMMAND ){
                showSelection( true );
            } else if( c == nextCommand ){
                if( mode == List.IMPLICIT ){
                    mode = List.EXCLUSIVE;
                } else if( mode == List.EXCLUSIVE ){
                    mode = List.MULTIPLE;
                } else {
                    mode = List.IMPLICIT;
                }
    
                display.setCurrent( new SampleList( 
                                             mode ) );
            }
        }

        private void showSelection( boolean implicit ){
            Alert alert = new Alert( 
                       implicit ? "Implicit Selection"
                               : "Explicit Selection" );
            StringBuffer buf = new StringBuffer();

            if( mode == MULTIPLE ){
                boolean[] selected = new boolean[ size() ];
                getSelectedFlags( selected );

                for( int i = 0; i < selected.length; ++i ){
                    if( selected[i] ){
                        if( buf.length() == 0 ){
                            buf.append( 
                             "You selected: " );
                        } else {
                            buf.append( ", " );
                        }

                        buf.append( getString( i ) );
                    }
                }

                if( buf.length() == 0 ){
                    buf.append( "No items are 
                                 selected." );
                }
            } else {
                buf.append( "You selected " );
                buf.append( getString( 
                       getSelectedIndex() ) );
            }

            alert.setString( buf.toString() );
            alert.setTimeout( Alert.FOREVER );

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


ENUMERATING, FILTERING, AND SORTING MIDP RECORD STORES

The Record Management System (RMS), part of the Mobile Information Device Profile (MIDP), lets you store arbitrary byte arrays as records in a database. The set of records is referred to as a record store. The RMS assigns a record ID to each record when it is created. The record ID is an integer greater than or equal to 1. The application uses this ID to get and set the content of a record.

Record IDs are unique within a record store. In other words, after a record ID has been assigned to a record, the record ID is not reused, even if that record is deleted. This makes it easy to track individual records. It does, however, pose a problem: how do you get a list of all the records in a record store, given that they aren't necessarily in a continuous, sequential order? The answer is to use an enumeration.

The RecordStore class defines an enumerateRecords method that returns a RecordEnumeration. RecordEnumeration is an interface whose purpose is similar to the java.util.Enumeration interface, but with more capabilities. (The RecordEnumeration interface, like all RMS classes and interfaces, is defined in the javax.microedition.rms package). Here is the definition for RecordEnumaration:

public interface RecordEnumeration {
  void destroy();
  boolean hasNextElement();
  boolean hasPreviousElement();
  boolean isKeptUpdated();
  void keepUpdated( boolean keepUpdated );
  byte[] nextRecord() throws InvalidRecordIDException, 
                  RecordStoreNotOpenException, 
                  RecordStoreException;
  int nextRecordId() throws InvalidRecordIDException;
  int numRecords(); 
  byte[] previousRecord() throws InvalidRecordIDException, 
                  RecordStoreNotOpenException, 
                  RecordStoreException;
  int previousRecordId() throws InvalidRecordIDException;
  void rebuild();
  void reset();
}

RecordEnumeration lets you traverse through a set of records. Unlike Enumeration, however, RecordEnumeration allows for bidirectional traversal of that set -- you can go backwards through the set of records as well as forwards. The set of records to traverse is defined by the enumerateRecords method.

To iterate over the entire set of records in a record store, do the following:

RecordStore rs = .....; // an open record store
RecordEnumeration enum = null;

try {
    enum = rs.enumerateRecords( null, null, false );
    while( enum.hasMoreElements() ){
        int id = enum.getNextRecordId();
        // do something here with the record
    }
}
catch( RecordStoreException e ){
}
finally {
    enum.destroy();
}

The first two arguments to the enumerateRecords method are used to filter and sort the records in an enumeration. (This tip will discuss filtering and sorting shortly.) The last argument determines whether the enumeration tracks changes to the record store. If the last argument is true, the enumeration updates itself as records are added to or deleted from a record store. This can be an expensive operation, however, and for most applications tracking is not required. Note that enumerateRecords throws RecordStoreNotOpenException if the record store is not open. RecordStoreNotOpenException is a subclass of RecordStoreException.

RecordEnumeration defines two methods to traverse forward through the enumeration: getNextRecordId and getNextRecord. It also defines two methods to traverse backwards: getPreviousRecordId and getPreviousRecord. Each method moves to the next or previous record in the enumeration, returning either the ID of the record (getNextRecordId and getPreviousRecordId) or its contents (getNextRecord and getPreviousRecord). An application calls hasNextElement or hasPreviousElement to determine if there are any more records to traverse in a specific direction.

You can reset the enumeration at any time by calling reset.

When an application is done with an enumeration it must call the enumeration's destroy method. This frees up any resources used by the enumeration.

If the first two arguments to enumerateRecords are null, all the records in the record store are returned in an undefined order. To return a subset of the records or to return the records in a specific order, you must define a filter, a comparator, or both. To do this, pass in objects that implement the RecordFilter or RecordComparator interfaces, depending on whether you defined a filter, comparator, or both.

The RecordFilter interface defines a single method:

public interface RecordFilter {
    boolean matches( byte[] recordData );
}

A filter is an instance of a class that implements the RecordFilter interface. If a filter is defined in the call to enumerateRecords, the filter's matches method is called for each record in the record store. The matches method determines if the record is included in the enumeration. Here, for example, is a filter that filters out empty records or records whose first byte is set to zero:

public class MyFilter implements RecordFilter {
  public boolean matches( byte[] recordData ){
    return( recordData.length > 0 && recordData[0] != 0 );
    }
}

Use the filter like this:

enum = rs.enumerateRecords( new MyFilter(), null, false );

Note that the filter is passed the contents of a record, but not the record ID or a reference to the record store itself.

The RecordComparator interface also defines a single method:

public interface RecordComparator {
    boolean compare( byte[] rec1, byte[] rec2 );
    
    int EQUIVALENT = 0;
    int FOLLOWS = 1;
    int PRECEDES = -1;
}

A comparator is an instance of a class that implements the RecordComparator interface. If a comparator is defined in the call to enumerateRecords, the enumerator sorts the records in the set. The enumerator calls the comparator's compare method to determine the relative order of two records. The comparator returns EQUIVALENT if both records are equivalent, FOLLOWS if the first record follows the second record, or PREVIOUS is the first precedes the second record. Here is a comparator that sorts records based on their length:

public class MyComparator implements RecordComparator {
    public boolean compare( byte[] rec1, byte[] rec2 ){
        if( rec1.length == rec2.length ){
            return EQUIVALENT;
        } else if( rec1.length < rec2.length ){
            return PRECEDES;
        } else {
            return FOLLOWS;
        }
    }
}

Of course, most comparators would compare records based on their contents. Use the comparator like this:

enum = rs.enumerateRecords( null, new MyComparator(), false );

Again, only the contents of the records to compare are passed to the comparator.

Both a filter and a comparator can be associated with an enumeration. The filter is called first to determine which records are in the enumeration. The comparator is then called to sort those records.

If tracking is enabled, filtering and sorting occurs whenever the record store is modified. This can be quite expensive to perform. You can enable or disable an enumeration's tracking of the underlying record store by using the keepUpdated method.

If tracking is disabled but you want to "refresh" the enumeration from the current set of records in the record store, call the rebuild method.

Let's end this tip with a simple MIDlet that demonstrates how to fill a List component from a record store. The records in the record store contain the first and last names of a group of people. The MIDlet includes a screen that lets you choose how to sort and filter the records.

import java.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.rms.*;

// Demonstrates simple record sorting and filtering

public class EnumDemo extends MIDlet
                      implements CommandListener {

    // Data members we need

    private Display              display;
    private RecordStore          rs;
    private EnumList             enumListScreen;
    private SortOptions          sortOptionsScreen;
    private String               firstName;
    private String               lastName;
    private byte[]               data = new byte[200];
    private ByteArrayInputStream bin = 
      new ByteArrayInputStream( data );
    private DataInputStream      din = 
      new DataInputStream( bin );

    // Define the four commands we use

    private Command exitCommand = new Command( "Exit",
                                    Command.EXIT, 1 );
    private Command sortCommand = new Command( "Sort",
                                  Command.SCREEN, 1 );
    private Command cancelCommand = new Command( "Cancel",
                                  Command.CANCEL, 1 );
    private Command okCommand = new Command( "OK", 
                                      Command.OK, 1 );

    // Define the sorting modes

    private static final int SORT_NONE = 0;
    private static final int SORT_FIRST_LAST = 1;
    private static final int SORT_LAST_FIRST = 2;

    // Define the filtering modes

    private static final int FILTER_NONE = 0;
    private static final int FILTER_STARTSWITH = 1;
    private static final int FILTER_CONTAINS = 2;

    // A simple class to hold the parts of a record

    private static class Record {
        String firstName;
        String lastName;
    }

    // Precanned names

    private static final String[][] names = {
        { "Eric", "Giguere" },
        { "John", "Doe" },
        { "Lisa", "Munro" },
        { "Stephanie", "Kwaly" },
        { "Dave", "Boyer" },
        { "Gwen", "Stephens" },
        { "Jennifer", "Lopert" },
        { "Ed", "Ort" },
    };

    // Constructor

    public EnumDemo(){
    }

    // Clean up

    protected void destroyApp( boolean unconditional )
                   throws MIDletStateChangeException {
        exitMIDlet();
    }

    // Close the record store for now

    protected void pauseApp(){
        closeRecordStore();
    }

    // Initialize things and reopen record store 
    // if not open

    protected void startApp() throws 
      MIDletStateChangeException {
        if( display == null ){ // first time called...
            initMIDlet();
        }

        if( rs == null ){
            openRecordStore();
        }
    }

    // Once-only initialization code

    private void initMIDlet(){
        display = Display.getDisplay( this );
        enumListScreen = new EnumList();
        sortOptionsScreen = new SortOptions();

        if( openRecordStore() ){
            enumListScreen.resort();
            display.setCurrent( enumListScreen );
        }
    }

    // Termination code

    public void exitMIDlet(){
        closeRecordStore();
        notifyDestroyed();
    }

    // Add a first-last name pair to the record store

    private void addName( String first, String last,
                          ByteArrayOutputStream bout,
                          DataOutputStream dout ){
        try {
            dout.writeUTF( first );
            dout.writeUTF( last );
            dout.flush();
            byte[] data = bout.toByteArray();
            rs.addRecord( data, 0, data.length );
            bout.reset();
        }
        catch( Exception e ){
        }
    }

    // Fill record store with some precanned names

    private void fillRecordStore(){
        ByteArrayOutputStream bout =
          new ByteArrayOutputStream();
        DataOutputStream dout = 
          new DataOutputStream( bout );

        for( int i = 0; i < names.length; ++i ){
            addName( names[i][0], names[i][1], bout, 
                                             dout );
        }
    }

    // Open record store, if empty then fill it

    private boolean openRecordStore(){
        try {
            if( rs != null ) closeRecordStore();

            rs = RecordStore.openRecordStore( 
                            "EnumDemo", true );
            if( rs.getNumRecords() == 0 ){
                fillRecordStore();
            }
            return true;
        }
        catch( RecordStoreException e ){
            return false;
        }
    }

    // Close record store

    private void closeRecordStore(){
        if( rs != null ){
            try {
                rs.closeRecordStore();
            }
            catch( RecordStoreException e ){
            }

            rs = null;
        }
    }

    // Move to and read in a record

    private boolean readRecord( int id, Record r ){
        boolean ok = false;

        r.firstName = null;
        r.lastName = null;

        if( rs != null ){
            try {
                rs.getRecord( id, data, 0 );
                r.firstName = din.readUTF();
                r.lastName = din.readUTF();
                din.reset();

                ok = true;
            }
            catch( Exception e ){
            }
        }

        return ok;
    }

    // Event handling

    public void commandAction( Command c, Displayable d ){
        if( c == exitCommand ){
            exitMIDlet();
        } else if( c == sortCommand ){
            display.setCurrent( sortOptionsScreen );
        } else if( d == sortOptionsScreen ){
            if( c == okCommand ){
                enumListScreen.resort();
            }
            
            display.setCurrent( enumListScreen );
        }
    }

    // Main screen -- a list of names

    class EnumList extends List
                   implements RecordComparator, 
                                 RecordFilter {
        private int    sortBy;
        private int    filterBy;
        private String filterText;
        private Record r1 = new Record();
        private Record r2 = new Record();

        // Constructor

        EnumList(){
            super( "Enum Demo", IMPLICIT );
            addCommand( exitCommand );
            addCommand( sortCommand );
            setCommandListener( EnumDemo.this );
        }

        // Resort the data and refill the list

        public void resort(){
          sortBy = sortOptionsScreen.getSortType();
          filterBy = sortOptionsScreen.getFilterType();
          filterText = sortOptionsScreen.getFilterText();

            RecordComparator comparator = null;
            RecordFilter     filter = null;

            if( sortBy != 0 ){
                comparator = this;
            }

            if( filterBy != 0 ){
                filter = this;
            }

            deleteAll();

            try {
              RecordEnumeration enum = rs.enumerateRecords( 
                          filter, comparator, false );
              Record            r = new Record();

              while( enum.hasNextElement() ){
                    int id = enum.nextRecordId();
                    if( readRecord( id, r ) ){
                      if( sortBy == SORT_LAST_FIRST ){
                            append( r.lastName + ", " + 
                                    r.firstName, null );
                        } else {
                            append( r.firstName + " " + 
                                     r.lastName, null );
                        }
                    }
                }

                enum.destroy();
            }
            catch( RecordStoreException e ){
            }
        }

        // Delete all items in the list

        private void deleteAll(){
            int n = size();
            while( n > 0 ){
                delete( --n );
            }
        }

        // The comparator

        public int compare( byte[] rec1, byte[] rec2 ){
            try {
                ByteArrayInputStream  bin = 
                     new ByteArrayInputStream( rec1 );
                DataInputStream       din = 
                           new DataInputStream( bin );

                r1.firstName = din.readUTF();
                r1.lastName = din.readUTF();

                bin = new ByteArrayInputStream( rec2 );
                din = new DataInputStream( bin );

                r2.firstName = din.readUTF();
                r2.lastName = din.readUTF();

                if( sortBy == SORT_FIRST_LAST ){
                    int cmp = r1.firstName.compareTo( 
                                       r2.firstName );
                 System.out.println( r1.firstName + 
                  " compares to " + r2.firstName + 
                                    " gives " + cmp );
                    if( cmp != 0 ) return ( 
                       cmp < 0 ? PRECEDES : FOLLOWS );
                    cmp = r2.lastName.compareTo( r2.lastName );
                    if( cmp != 0 ) return ( 
                       cmp < 0 ? PRECEDES : FOLLOWS );
                } else if( sortBy == SORT_LAST_FIRST ){
                    int cmp = r1.lastName.compareTo( 
                                        r2.lastName );
                    if( cmp != 0 ) return ( 
                        cmp < 0 ? PRECEDES : FOLLOWS );
                    cmp = r2.firstName.compareTo( 
                                        r2.firstName );
                    if( cmp != 0 ) return ( 
                                  cmp < 0 ? PRECEDES : 
                                            FOLLOWS );
                }
            }
            catch( Exception e ){
            }

            return EQUIVALENT;
        }

        // The filter

        public boolean matches( byte[] rec ){
            try {
                ByteArrayInputStream  bin = 
                      new ByteArrayInputStream( rec );
                DataInputStream       din = 
                           new DataInputStream( bin );

                r1.firstName = din.readUTF();
                r1.lastName = din.readUTF();

                if( filterBy == FILTER_STARTSWITH ){
                    return( r1.firstName.startsWith( 
                                         filterText ) 
                            ||
                          r1.lastName.startsWith( 
                                       filterText ) );
                } else if( filterBy == 
                  FILTER_CONTAINS ){
                    return( r1.firstName.indexOf( 
                                     filterText ) 
                            >= 0 ||
                        r1.lastName.indexOf( 
                                  filterText ) >= 0 );
                }
            }
            catch( Exception e ){
            }

            return false;
        }
    }

    // The options screen for choosing which sort and
    // filter modes to use, including the filter text

    class SortOptions extends Form {

        // Constructor

        SortOptions(){
            super( "Sort Options" );
            addCommand( okCommand );
            addCommand( cancelCommand );
            setCommandListener( EnumDemo.this );

            append( sortTypes );
            append( filterTypes );
            append( filterText );
        }

        // Return current sort type

        public int getSortType(){
            return sortTypes.getSelectedIndex();
        }

        // Return current filter type

        public int getFilterType(){
            return filterTypes.getSelectedIndex();
        }

        // Return current filter text

        public String getFilterText(){
            return filterText.getString();
        }

        // Labels and user interface components

        private String[] sortLabels = 
                         { "None", "First Last", 
                                "Last, First" };

        private String[] filterLabels = 
                           { "None", "Starts With",  
                                      "Contains" };

        private ChoiceGroup sortTypes = 
                                 new ChoiceGroup( 
                                        "Sort Type:",
                                ChoiceGroup.EXCLUSIVE,
                                   sortLabels, null );

        private ChoiceGroup filterTypes = 
                                 new ChoiceGroup( 
                                       "Filter Type:",
                                ChoiceGroup.EXCLUSIVE,
                                 filterLabels, null );

        private TextField filterText = new TextField( 
                                       "Filter Text:",
                                        null, 20, 0 );
    }
}


— Note —

Sun respects your online time and privacy. The Java Developer Connection mailing lists are used for internal Sun MicrosystemsTM purposes only. You have received this email because you elected to subscribe. To unsubscribe, go to the Subscriptions page, uncheck the appropriate checkbox, and click the Update button.

As of May 22, 2001, Sun Microsystems updated its Privacy Policy to give you a better understanding of Sun's Privacy Policy and Practice. If you have any questions, contact privacy@sun.com.

— Subscribe —

To subscribe to a JDC newsletter mailing list, go to the Subscriptions page, choose the newsletters you want to subscribe to, and click Update.

— Feedback —

Comments? Send your feedback on the J2ME Tech Tips to:

jdc-webmaster@sun.com

— Archives —

You'll find the J2ME Tech Tips archives at:

http://java.sun.com/jdc/J2METechTips/index.html

— Copyright —

Copyright 2001 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This Document is protected by copyright. For more information, see:

http://java.sun.com/jdc/copyright.html

- LINKS TO NON-SUN SITES

The J2ME Tech Tips may provide, or third parties may provide, links to other Internet sites or resources. Because Sun has no control over such sites and resources, You acknowledge and agree that Sun is not responsible for the availability of such external sites or resources, and does not endorse and is not responsible or liable for any Content, advertising, products, or other materials on or available from such sites or resources. Sun will not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such Content, goods or services available on or through any such site or resource.

J2ME Tech Tips June 22, 2001

Sun, Sun Microsystems, Java, Java Developer Connection, and J2ME and are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.


Print Button
[ This page was updated: 21-Jun-2001 ]
Products & APIs | Developer Connection | Docs & Training | Online Support
Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary | Feedback | A-Z Index
For more information on Java technology
and other software from Sun Microsystems, call:
(800) 786-7638
Outside the U.S. and Canada, dial your country's AT&T Direct Access Number first.
Sun Microsystems, Inc.
Copyright © 1995-2001 Sun Microsystems, Inc.
All Rights Reserved. Terms of Use. Privacy Policy.