Skip to main content

Using Styles, Themes, and Painters with LWUIT

April 19, 2010

{cs.r.title}







Lightweight User Interface
Toolkit (LWUIT)
version 1.3, released in December 2009,
consolidates the modifications over version 1.1 initiated in
version 1.2 and incorporates some new ones too. It also introduces
three new components -- Tree, Table and Spinner. The use of the
Tree widget is demonstrated through the LWUIT Demo that
comes with the "http://java.sun.com/javame/technology/lwuit/">LWUIT download
bundle. In this article we examine the changes with respect to
Style and go on to check out Table and Spinner.

The demo applications have been developed on the "http://developer.sprint.com/site/global/develop/mobile_platforms/java_me/javame.jsp">
Sprint Wireless Toolkit 3.3.2
(SWTK). Not only does this
toolkit support LWUIT extremely well, it also has an interesting
array of device emulators like those for HTC Touch and Samsung
Instinct. If you intend to try out LWUIT, I would strongly suggest
that you install the Sprint WTK on your computer.

Style

The idea behind Style is to centrally define the visual
attributes for each widget (component). In addition to its physical
design, such as its shape, the appearance of a widget can be defined
in terms of a number of common features or attributes. In LWUIT 1.1
the settable attributes for a component were:

  • Background and foreground colors.
  • Fonts for writing text on it.
  • Background transparency.
  • Background image.
  • Margin and padding.
  • Background painters.
  • Border.

The additional attributes defined under LWUIT 1.3 are:

  • Background type -- specifies whether the background has an
    image or a color gradient. An image can be tiled, scaled or aligned
    (unscaled with alignment). A gradient for background can be linear
    (vertical/horizontal) or radial.
  • Background alignment -- if an image (tiled or aligned) is used
    for the background, the alignment (top/bottom/left/right/center) is
    defined by this attribute.
  • Background gradient start and end colors.

A Style object holds all these attributes for each
state of each component that is used in an application, and has
appropriate accessor methods. In addition this class also has the
ability to inform a registered listener when a style object
associated with a given component is altered.

A Style for Each State

In the early days of LWUIT, that is up to version 1.1, there
used to be just one Style object for each widget to be
displayed. This single Style object had separate
attributes corresponding to the different widget states. For
example, the background color for the unselected (unfocused) state
would be defined as bgColor and that for the selected
(focused) state as bgSelectionColor. The code for
setting these colors would look like this.


        //when the widget does not have focus
        //the background will be red
        myComponent.getStyle().setBgColor(0xff0000);

        //when the widget has focus
        //the background will be blue
        myComponent.getStyle().setBgSelectionColor(0x0000ff);

However, as of version 1.2, this has changed.
Components now have distinct styles for each of their permissible
states. Most components would have a selectedStyle and
an unselectedStyle. Components like buttons and those
that extend the Button class will have an additional style
corresponding to the pressed state -- the
pressedStyle. So, the above code sample will now look
as follows.


        //when the widget does not have focus
        //the background will be red
        myComponent.getUnselectedStyle().setBgColor(0xff0000);

        //when the widget has focus
        //the background will be blue
        myComponent.getSelectedStyle().setBgColor(0x0000ff);

Note that we have used two different versions of the
getter method -- one for each style. Actually, the
Component class has a getStyle method too,
which returns the appropriate style depending upon the state of the
component. This capability leads to simpler coding especially for
methods that render components.

When a component is created, a default Style object
gets associated with it as its unselectedStyle. The
other styles are created when accessed for the first time through
the corresponding setter or getter methods.

There are several ways for setting and modifying attributes as
shown in the following list.

  • By using the get*Style().set* combination as
    above.
  • By creating a new style with the desired attributes and then
    invoking the set*Style method to set the new
    style.
  • Style for an entire set of widgets can be set through the
    UIManager instance. The relevant technique is
    
            //method for setting unselected style
            UIManager.getInstance().setComponentStyle
                    (String id, Style style)
    
            //method for setting selected style
            UIManager.getInstance().setComponentSelectedStyle
                    (String id, Style style)
    

    However, currently this approach works for selected and unselected
    styles.
  • Through a Theme.

In the following section we shall check out a simple demo
application which shows how styling can be done under LWUIT
1.3.

Using Style

Our application will have a single form with three widgets and
will look like the Figure 1:


Figure 1. Style Update Demo.

The screenshot above shows a screen with two labels and one
button. Labels, by default, cannot receive focus and the topmost
widget is such a label. The second label has been explicitly made
focusable as shown by the code snippet below.


        //create a label
        Label focusLabel = new Label("Focusable");

        //and make it focusable
        focusLabel.setFocusable(true);

In Figure 1 we see the second label in the selected state. The
applicable style is set as follows:


        //modify default style to set attributes 
        //for selected state of focus label
        focusLabel.getSelectedStyle().setBgColor(0x555555);
        focusLabel.getSelectedStyle().setFont(font);
        focusLabel.getSelectedStyle().setBorder
                (Border.createBevelRaised());

The first label and the button are unselected and their
appearances are styled through the same Style object.
This object has the following attributes:


        //create a common style for unselected state
        Style unselStyle = new Style();
        unselStyle.setFont(font);
        unselStyle.setBgTransparency(64);
        unselStyle.setFgColor(0xffc605);
        unselStyle.setBorder(Border.createEtchedRaised());

This style is set as the unselected one for all labels by
calling the setComponentStyle(String id, Style style)
method. This is shown below.


        //set unselected style for labels
        UIManager.getInstance().setComponentStyle
                ("Label", unselStyle);

The unselected style for the button is set in the following way
using the same style object that is used for labels.


        //create button
        Button button = new Button("Button");

        //set style for unselected state of button
        button.setStyle(unselStyle);

Figure 2 shows the same screen as above with the
button in the selected state.


Figure 2. The button is selected.

We can see that both labels are unselected and their appearances
are the same. The button, which has focus, looks just like
focusLabel in Figure 1. This is ensured by creating a
style with attributes that have the same values that were used for
the selected style of focusLabel. This new style --
buttonSelStyle -- is then installed as the selected
style for the button.


        //create selected style for button
        Style buttonSelStyle = new Style();
        buttonSelStyle.setBgColor(0x555555);
        buttonSelStyle.setFont(font);
        buttonSelStyle.setBorder(Border.createBevelRaised());
        ...
        //set style for selected state of button
        button.setSelectedStyle(buttonSelStyle);

In a similar manner we create and set a style for the pressed
state of the button:


        //create pressed style for button
        Style buttonPrStyle = new Style();
        buttonPrStyle.setBgColor(0xff0055);
        buttonPrStyle.setFont(font);
        buttonPrStyle.setBgTransparency(127);
        buttonPrStyle.setBorder(Border.createRoundBorder
                (12, 5, 0xff0000));
        ...
        //set style for pressed state of button
        button.setPressedStyle(buttonPrStyle);

The result of pressing the button can be seen in Figure 3
below.


Figure 3. The button is pressed.

In the demo application above, style attributes have been set
programatically to show some fundamental aspects of styling. In
real life applications, though, it would be advisable to apply
themes.

The Theme Creator

In the beginning there was the Resource Editor -- a very
handy tool for creating and editing resource files. In Decemeber
2008 it was renamed LWUIT Designer, and in the latest
release it has become the Theme Creator. While the
essential functionality remains the same, both the LWUIT
Designer 1.2
and the Theme Creator allow the
creation of three distinct styles, as we can see in Figure 4.


Figure 4. Creating a theme with three styles.

The Theme Creator now provides support for SVG images
too. This is shown in Figure 5.


Figure 5. SVG support.

UIID

Every component can have its own unique id which is used for
styling as we have seen. All components in the library have their
ids. When creating a custom component, a new id should be alloted.
Sometimes, components are built up using existing ones and these
constituent parts can have ids different from their original ones.
Consider the case of a component that uses a label to form a part.
If we want to give this part a distinct id for styling, the
getUIID method of Component class would
have had to be overridden in the days of LWUIT 1.1. A new method --
setUIID -- was provided in LWUIT 1.2 to do the same
thing in a simpler and more intuitive way. So now we can
write:


        Label newMyPart = new Label();
        newMyPart.setUIID("NewMyPart");

Spinner

A spinner is similar to a combo box in the sense that it shows a
single value from a list of values. However, spinner does not show
a drop down list for selection. Instead the up or
down key has to be used to scroll through the list. Also, a
spinner can show only a date, time or numerical value. Figure 6
shows a form with two components: a button and a spinner. The
button has been added to show how different styles take effect
depending on whether the spinner has focus or not. Here the spinner
is shown in the unselected state.


Figure 6. Spinner is
unselected

The Spinner class does not have an accessible
constructor. One of the factory methods has to be used to get an
instance of spinner. In this case we create a spinner to display
time in twenty-four hour format showing hours, minutes and seconds.
The method used is:

createTime(int min, int max, int
currentValue, int step, boolean twentyFourHours, boolean
showSeconds)
. The first two parameters specify the minimum
and maximum values for the list. The third parameter defines the
value to be shown initially. The unit for all the three is seconds
since midnight. The fourth is the difference between two successive
values in seconds. The fifth and sixth parameters define the
display format.

The image on the right of the time display is called the
spinnerHandle and is meant to indicate that the
display can spin. The following code snippet shows how the
spinner is created and the spinnerHandle is set. A
word about the value initially shown in Figure 6 above: while
creating the spinner instance, the value specified for initial
display is 180, which corresponds to three minutes or, in the given
format, 00:03:00. However, note that the step size has been
specified as 40 which means 180 is not in the list. The time
displayed corresponds to 160 which is the highest value not
exceeding 180.


        //create spinner with seconds display
        spinner = Spinner.createTime(0, 7200, 180, 40, true, true);

        //set image to visually indicate it is spinnable
        try
        {
                spinner.setSpinnerHandle(Image.createImage("/handle.png"));
        }
        catch(java.io.IOException ioe)
        {
        }

In Figure 6 we see the spinner in the
unselected state. There is a dark olive green background on which
the numerical display is rendered in blue. The background
represents the spinner container and its unselected appearance is
set as follows:


        //affects look when spinner does not have focus
        spinner.getUnselectedStyle().setBgColor(0x556b3f);
        spinner.getUnselectedStyle().setBorder(Border.createBevelRaised());

The first step in setting style for the time display is to get
the DefaultListCellRenderer instance for the spinner.
The style attributes can then be set as shown below.


        //create a font
        Font font = Font.createSystemFont(Font.FACE_PROPORTIONAL,
                Font.STYLE_BOLD,Font.SIZE_LARGE);

        ...

        //affects look when spinner does not have focus
        spinner.getUnselectedStyle().setBgColor(0x556b3f);
        spinner.getUnselectedStyle().setBorder(Border.createBevelRaised());

        ...

        //renderer for the cell containing display
        DefaultListCellRenderer dlcr = 
                (DefaultListCellRenderer)spinner.getRenderer();

        //affects steady state look
        dlcr.getSelectedStyle().setBgColor(0x0000ff);
        dlcr.getSelectedStyle().setFgColor(0);
        dlcr.getSelectedStyle().setFont(font);
        dlcr.getSelectedStyle().setPadding(Component.RIGHT, 7);

        //affects look during scrolling
        dlcr.getUnselectedStyle().setBgTransparency(0);
        dlcr.getUnselectedStyle().setFgColor(0);
        dlcr.getUnselectedStyle().setFont(font);

The code above also sets the renderer style for the unselected
state. This style becomes effective when the spinner value scrolls.
To see how that works, press the down key. Now, the spinner
gets focus and its appearance changes as specified by the code
below.


        //affects look when spinner has focus
        spinner.getSelectedStyle().setBgColor(0x0000ff);
        spinner.getSelectedStyle().setBorder
                (Border.createRoundBorder(12, 5, 0xff0000));

The spinner now looks like what's shown in Figure 7.


Figure 7. Spinner is selected.

Although the spinner now has focus, it will not respond to the
up or the down key. At this stage the scrolling keys
will toggle focus between the spinner and the button. To direct the
scrolling key events to control the spinner display, the
select key has to be pressed while the spinner has focus.
Generally speaking, the select key can be used to toggle the
applicability of scrolling keys between a form itself and a
component with focus contained in the form. The scrolling keys can
now be used to sequentially change the time displayed.

What remains now is to see how we can retrieve the value
selected on the spinner. The demo does this in different ways. One
way is through a command. We add the Show command to the
form and, in the actionPerformed method, call into the
getValue method of Spinner
class.


        //create and add 'Exit' command to the form
        //the command id is 1
        demoForm.addCommand(new Command("Exit", 1));

        //create and add command to show spinner value
        //the command id is 2
        demoForm.addCommand(new Command("Show", 2));

        //this MIDlet is the listener for the form's command
        demoForm.setCommandListener(this);

        ...

        //act on the commands and the click
        public void actionPerformed(ActionEvent ae)
        {
                Command cmd = ae.getCommand();

                if(cmd != null)
                {
                        switch (cmd.getId())
                        {
                                //'Exit' command
                                case 1:
                                notifyDestroyed();
                                        break;

                                //'Show' command
                                case 2:
                                System.out.println("Value 
                                                shown through command : " 
                                                + spinner.getValue());
                        }

                        return;
                }

                ...
        }

A second method is to register the MIDlet as the
ActionListener for the spinner. If we now click on the
spinner, the actionPerformed method of the MIDlet will
be invoked. The necessary action can then be taken.

        
        //this MIDlet is the action listener for spinner
        spinner.addActionListener(this);

        ...

        //act on the commands and the click
        public void actionPerformed(ActionEvent ae)
        {
                Command cmd = ae.getCommand();

                if(cmd != null)
                {
                        ...

                        return;
                }

                if(ae.getSource() == spinner)
                {
                        System.out.println("Value shown on click : "
                         + spinner.getValue());
                }
        }

The third approach used in the demo is to add the MIDlet to the
spinner as the SelectionListener. For this technique
to work, the MIDlet has to implement the SelectionListener
interface so that its selectionChanged method is
called whenever the spinner selection is changed.


        //this MIDlet is the selection listener for spinner
        spinner.addSelectionListener(this);

        ...

        public void selectionChanged(int oldSelected, int newSelected)
        {
                if(oldSelected != newSelected)
                {
                        ListModel model = spinner.getModel();
                        System.out.println("Selection changed from " + 
                                model.getItemAt(oldSelected) + " to " +  
                                model.getItemAt(newSelected));
                }
        }

When we click on a list, its selection changes, and this causes
the selectionChanged method of the registered
SelectionListener to be called in addition to the
actionPerformed method of its
ActionListener. As Spinner is a subclass
of List, it behaves in a similar fashion and clicking
on it results in the printing of the selection change message for
the demo. For a spinner, however, we do not consider this to be a
valid selection change since the value remains the same. The
if clause in the above code listing takes care of this
issue. The resultant console printouts for all three approaches are
shown in Figure 8.


Figure 8. Message printouts.

Table

A table displays information in the form of a grid. The topmost
row describes the name of the data in each column. These are the
Headers. The rest of the table displays data and is
comprised of Cells. The headers are instances of
Label and are meant to be uneditable. The cells can be
either editable or uneditable. Editable cells are instances of
TextField while uneditable cells are instances of
Label. However, uneditable does not mean
unmodifiable as we shall see when we discuss the demo
application.

Figure 9 shows a table with 5 rows and 3
columns. The topmost row holds the headers. The rest of the rows
hold cells that display data. Here we have two kinds of data: text
in the leftmost column (column 0) and numbers in the other columns
(columns 1 and 2).


Figure 9. A table.

The contents of a table are determined by an underlying model --
the table model. LWUIT defines the TableModel
interface that must be implemented by a table model; the default
implementation included in the library is the
DefaultTableModel class. So the first step in creating
a table is to set up the model. This model is then passed as a
parameter to the table constructor. The relevant code for our
application is:


        //define headers
        String[] columns = new String[] {"Items", "Good", "Scrap"};

        //define data
        Object[][] data = new Object[][] {{"Plate-01", 
                new Integer(1605), new Integer(57)}, {"Plate-02", 
                new  Integer(5001), new Integer(326)}, {"Coil-01", 
                new Integer(417), new Integer(124)}, {"Coil-02", 
                new Integer(451), new  Integer(22)}};

        //create model with uneditable cells
        //as specified by last parameter
        //for editable cells last parameter must be 'true'
        DefaultTableModel model = new DefaultTableModel(columns, 
                data, false);

        //create table
        table = new Table(model);

The cells of our table are labels and the default alignment is
Label.LEFT. There is a method in the Table
class to set cell alignment -- setCellAlignment.
However, this is not a static method and works only for new
cells created for an existing table. The default alignment for
headers is Label.CENTER To change the alignment of
cells, we need to access each label that forms a cell and set its
alignment. Another point to be noted is that, for some form
layouts, the sizing of the last column may not work properly. We
have used a kind of rendering prototype to ensure proper
sizing. All this is shown in the code snippet below.


        //create a font
        Font font = Font.createSystemFont(Font.FACE_PROPORTIONAL,
                Font.STYLE_BOLD,Font.SIZE_LARGE);

        //width of 'rendering prototype'
        int w = font.stringWidth("WWWW");

        ...

        //does not affect alignment of cells already created as
        //table is constructed with cells with default alignment
        //works only for cells created later
        //this happens, for example, if new data is set
        table.setCellAlignment(Label.CENTER);

        TableLayout layout = (TableLayout)table.getLayout();
        Component c = null;

        //'getRowCount' does not take into account the header row
        //so add 1 to get total number of rows
        int rows = ((DefaultTableModel)table.getModel()).
                                getRowCount() + 1;
        int cols = ((DefaultTableModel)table.getModel()).
                                getColumnCount();

        for(int i = 0; i < rows; i++)
        {
                for(int j = 0; j < cols; j++)
                {
                        //get the component for cell at
                        //specified row and column
                        c = layout.getComponentAt(i,j);

                        if(i == 0)
                        {
                                //set border for headers
                                c.getUnselectedStyle().setBorder
                                        (Border.createLineBorder
                                        (1, 0xff0000));
                                if(j == cols - 1)
                                {
                                        //use prototype
                                        //to size the last column
                                        c.setPreferredSize(new Dimension(w, 
                                                c.getPreferredH()));
                                }
                        }
                        else
                        {
                                //necessary because cells were originally
                                //created with default (left) alignment
                                ((Label)c).setAlignment(Label.CENTER);
                        }
                }
        }

The header strings and the cell data are held within the table
model. The TableModel interface defines methods to
access these variables. Note that there is only a getter
method for the column names while there is a getter as well
as a setter method for the cell values. The Show
command on our demo form uses the getValueAt method to
print out a cell value on the console.


        //act on the commands
        public void actionPerformed(ActionEvent ae)
        {
                Command cmd = ae.getCommand();

                switch (cmd.getId())
                {
                        ...

                        //'Show' command
                        case 2:
                                //get the table model instance
                                DefaultTableModel dtm = 
                                        (DefaultTableModel)
                                        table.getModel();

                                //get the value at the specified cell
                                //use actual row number - 1 
                                //since actual first row is not for data
                                Object val = dtm.getValueAt(0, 2);

                                //print value
                                System.out.println
                                        ("Plate-01 Scrap : " + val);

                                ...

                }
        }

The printout is shown in Figure 10.


Figure 10. Accessing cell data.

The setValueAt method can be used to
programatically change data in a cell -- even for uneditable cells.
In our demo application the Show command is replaced with a
Modify command which changes the data in the cell for
Plate-02 Scrap from 326 to 750. The
setValueAt method leads to the creation of a new label
to replace the existing one. As we have already set cell alignment
to Label.CENTER, the new value remains properly
aligned.


                //create and add command to show value
                //the command id is 2
                //demoForm.addCommand(new Command("Show", 2));

                //create and add command to change value
                //the command id is 2
                demoForm.addCommand(new Command("Modify", 2));

                ...
        }

        ...

        //act on the commands
        public void actionPerformed(ActionEvent ae)
        {
                Command cmd = ae.getCommand();

                switch (cmd.getId())
                {
                        //'Exit' command
                        case 1:
                                notifyDestroyed();
                                break;

                        //'Show' command
                        /*case 2:
                                //get the table model instance
                                DefaultTableModel dtm = 
                                        (DefaultTableModel)
                                        table.getModel();

                                //get the value at the specified cell
                                //use actual row number - 1 
                                //since actual first row is not for data
                                Object val = dtm.getValueAt(0, 2);

                                //print value
                                System.out.println
                                                ("Plate-01 Scrap : " 
                                                + val);*/

                        //'Modify' command
                        case 2:
                                //get the table model instance
                                DefaultTableModel dtm = 
                                        (DefaultTableModel)
                                        table.getModel();

                                //set the value at the specified cell
                                //use actual row number - 1 
                                //since actual first row is not for data
                                dtm.setValueAt(1, 2, new Integer(750));
                }
        }

In Figure 11 we can see the new value for
Plate-02 Scrap.


Figure 11. Cell data modified.

In the demo application for table, the only styling done
programmatically is for the set of borders for the headers. The rest
of the styling has been defined in a theme. The theme file is shown
below in Figure 12. A word of caution here. This theme file has been created by
the Theme Creator that comes with LWUIT 1.3. The Resource
Editor
that comes with the SWTK is of an earlier vintage and
will not be able to open this file.


Figure 12. Theme file for the demo app.

Conclusion

We have seen how the approach to styling has been modified since
LWUIT 1.1. We have also checked out the new Theme Creator
and two new components that have been introduced in LWUIT 1.3.
However, there are a number of interesting new features that have
not been discussed here. These are listed in What's new in This
Release
in the LWUIT 1.3 download bundle.

Resources


width="1" height="1" border="0" alt=" " />
Biswajit Sarkar is an electrical engineer with specialization in Programmable Industrial Automation. Biswajit is the author of "LWUIT 1.1 for Java ME Developers" published by PACKT Publishing.
AttachmentSize
Art14_FocusedButton.png41.21 KB
Art14_FocusedLabel.png41.32 KB
Art14_FocusedSpinner.png41.11 KB
Art14_Modified1.png41.38 KB
Art14_NewTheme.png2.94 KB
Art14_PressedButton.png41.06 KB
Art14_SpinnerValues.png33.92 KB
Art14_SVG.png10.65 KB
Art14_Table.png41.5 KB
Art14_TableValue.png21.58 KB
Art14_ThemeFile.png51.61 KB
Art14_UnfocusedSpinner.png40.96 KB
Art14_src_codes.zip6.99 KB
Related Topics >> GUI   |   J2ME   |   Mobility   |   Programming   |   Featured Article   |