Exploring the ListField, Part 1

Display data with the BlackBerry ListField

Lists

We cannot ignore them — they are everywhere.

Lists of things “todo”. Things to buy. Choices to choose from.

No mobile environment would be complete without the ability to display a list of choices to a user.

Of course, every platform has their own name for them. Android calls its list a ListView and requires an “adapter” to supply the data to the list widget.

iPhone developers create lists with the UITableView and flesh out a “protocol” in Objective-C to provide the data via a callback paradigm. For the Java programmers among us, a protocol is similar to an Interface in Java.

In an earlier article we even looked at how to work with lists on the WebOS platform — back when we had high hopes for the OS.

With the introduction of the BlackBerry Torch and some new energy in the BlackBerry platform, I thought it would be worthwhile to look at programming the ListField for the BlackBerry OS.

Here is a road map of this and the next two articles in this series where we look at three progressive examples of working with the ListField:

  1. Providing data via the ListFieldCallback interface (this article)
  2. Subclassing and adding color to the list so it doesn’t look so bland
  3. Displaying data from a persistent data source

Using the ListField

There are two files in the sample project:

  • BBColorList.java — this is the entry point of the application, which extends UiApplication.
  • BBColorScreen.java — this file contains the class BBColorScreen, which extends MainScreen. This is the primary UI for the sample application.

Here is the code for the BBColorList class.

/*
 * BBColorList.java
 *
 */
package com.msi.linuxmag.cl;

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

class BBColorList extends UiApplication {
    BBColorList() {
        pushScreen(new BBColorScreen());
    }

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

This code is boiler-plate for a BlackBerry app — the entry point of the application is the main() function which creates an instance of the BBColorList app and then enters the main event handling loop. The BBColorList class creates an instance of the BBColorScreen class and “pushes” it to the foreground with a call to pushScreen. Not very exciting, but necessary. Let’s move on to the BBColorScreen class.

/*
 * BBColorScreen.java
 *
 */

package com.msi.linuxmag.cl;

import net.rim.device.api.ui.*;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.ListField;
import net.rim.device.api.ui.component.ListFieldCallback;

class BBColorScreen extends MainScreen implements ListFieldCallback{
    ListField sampleList;
    String [] listData = {"ALICEBLUE","ANTIQUEWHITE","AQUA","AQUAMARINE", \
"AZURE","BEIGE","BISQUE","BLACK","BLANCHEDIAMOND"};

    BBColorScreen() {
        super(DEFAULT_MENU);
        sampleList = new ListField();
        sampleList.setSize(listData.length);
        sampleList.setCallback(this);
        add(sampleList);
        setTitle(new LabelField("Linux Mag ListField",LabelField.USE_ALL_WIDTH | DrawStyle.HCENTER));
    }
    /* ListFieldCallback Interface methods    */
    public void drawListRow(ListField listField,Graphics graphics,int index,int y,int width)
    {
        graphics.setFont(Font.getDefault());
        graphics.drawText(listData[index],2,y,DrawStyle.TOP,width);
    }

    public int getPreferredWidth(ListField listField)
    {
        return Display.getWidth();
    }
    public Object get(ListField listField,int index)
    {
        return listData[index];
    }

    public int indexOfList(ListField listField,String prefix,int start)
    {
        try {
            int ret = -1;
            for (ret = start;ret<listField.getSize();ret++) {
                if (listData[ret].toUpperCase().startsWith(prefix.toUpperCase())) return ret;
            }
            return 0;
        } catch (Exception e) {
            return 0;
        }
    }
    /* End of ListFieldCallback Interface methods */
}

Managing data with the ListField
Managing data with the ListField

The first thing to note about this class is that it extends the MainScreen class and implements the ListFieldCallback interface. The ListFieldCallback is responsible for supplying data to an associated ListField instance.

Let’s take a moment to discuss the relationship between a ListField and its data. The ListField is responsible for managing a “list” of entries, though it knows very little about the data itself. In fact, the only thing it is aware of is the number of entries it contains. In the sample code, the number of entries in the list is assigned through the setSize() method of the ListField. Alternatively, the size may be passed in to the constructor of the ListField.

When the ListField displays data at runtime, it invokes a method named drawListRow() from its associated callback. The drawListRow is one of four methods required by the ListFieldCallback interface:

  1. drawListRow — used to draw the text/image onto the list within the specified coordinates.
  2. getPreferredWidth — used to tell the list how wide it should be drawn. This is typically the full width of the screen.
  3. get — used to retrieve an entry from the list. Note that its return type is Object.
  4. indexOfList — this is used to help find an entry from the list.

Jumping back into the code, let’s examine the constructor: BBColorScreen().

This method invokes the base class’ constructor by calling super() and then proceeds to wire up the ListField. This declarative approach to user interface design is typical of BlackBerry applications — you either like it or you don’t. I tend to prefer this over WYSIWYG because I’m a bit of a control freak and like to be able to see everything plainly in code rather than having to worry if I refreshed a separate resource file. At times it can be tedious to define everything “by hand”, I confess.

Once the ListField is created, we tell the list how large it is with a call to setSize(), passing in the number of entries in our array. If we were working with a database (called a RecordStore in BlackBerry-speak), we would likely pass in the number of records available in a RecordEnumeration, which is similar to a “record set”.

Next we assign the callback to be “this” class itself.

We add this ListField to the screen with a call to the add() method and then we assign a LabelField to be the title of the screen. The reason we use a LabelField and not just simple String literal is because we can take advantage of the drawing styles to center the text on the screen. If we had just called setTitle(“some title”), which is a valid approach, the title would be aligned to the left. Just a personal taste issue.

Ok, now we’re ready to look at the methods which are implemented for the ListFieldCallback interface. The first thing to observe about these four methods is that they each pass in an instance of a ListField. This allows the same class to provide data for multiple instances of a ListField at runtime should you choose to organize your code in this fashion. Our code has only a single ListField so it really doesn’t matter to us in this example.

The drawListRow() method takes the most arguments: a ListField instance,a Graphics object, an integer representing the ordinal of the desired list element, a vertical offset and the width available to draw with. This information tells us which object to draw and what kind of boundary we are constrained to stay within.

 public void drawListRow(ListField listField,Graphics graphics,int index,int y,int width)
    {
        graphics.setFont(Font.getDefault());
        graphics.drawText(listData[index],2,y,DrawStyle.TOP,width);
    }

In this method we assign the selected font with a call to setFont() and then draw the string via the drawText method. Pretty simple. The follow-on articles spend some more time in this method customizing the drawing activity.

Let’s jump to the getPreferredWidth() method. This method passes in a ListField instance and expects an integer return value. Because we want our ListField to span the entire width of the screen, we return Display.getWidth(). The Display class is part of the net.rim.device.api.system package and is a protected API. This means that the application must be signed before running on a physical device.

 public int getPreferredWidth(ListField listField)
    {
        return Display.getWidth();
    }

The get() method takes a ListField and integer parameter. The data behind our ListField is just an array of String objects so we can simply return the String associated with the index requested.

 public Object get(ListField listField,int index)
    {
        return listData[index];
    }

The indexOfList method takes three parameters: a ListField, a String to search for and a starting index. Implementing this function provides the functionality of searching a ListField by spelling out the desired entry.

Progressive Search Feature: searching for 'bla
Progressive Search Feature: searching for ‘bla

For example, if you have a long list and want to jump down to the entries that start with the letters “BLA”, you will jump to the first list entry which starts with those letters. The user must type fairly quickly to get the progressive search. The key to the progressive search is the start parameter. If you wait too long between keypresses, the search starts again from the top of the list.

The implementation of this code starts by indexing into the listData array at the start index and then performs a case-insensitive search against the elements of the array. If found, it returns the index of the desired element. If not, it returns 0, causing the focus to jump to the top of the list.

    public int indexOfList(ListField listField,String prefix,int start)
    {
        try {
            int ret = -1;
            for (ret = start;ret<listField.getSize();ret++) {
                if (listData[ret].toUpperCase().startsWith(prefix.toUpperCase())) return ret;
            }
            return 0;
        } catch (Exception e) {
            return 0;
        }
    }

Mastering the ListField is a core skill for any BlackBerry developer. Stay tuned for our next article which looks at making the list entries a bit more attractive.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62