Skip to main content

Using Styles, Themes, and Painters with LWUIT

September 25, 2008

{cs.r.title}







The Lightweight User Interface
Toolkit
(LWUIT) introduces a number of impressive
functionalities to the "http://java.sun.com/javame/index.jsp">Java ME UI developer.
Styles, themes, and painters are three such functionalities that
facilitate the development of highly attractive and device-independent visual elements. In this article, we see how to use them
and explore some of the subtle issues.

The demo applications have been developed on the "http://developer.sprint.com/site/global/develop/technologies/java_me/p_java_me.jsp">
Sprint Wireless Toolkit 3.3.1
. 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 concept of style is the foundation on which
theming is built. The idea behind style is to centrally
define the visual attributes for each 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:

  • Background and foreground colors: Each component has
    four color attributes: two each for background and foreground. A
    component is considered selected when it is ready for activation.
    When a button receives focus, for example, it is in the selected
    state and can be activated by being clicked. A component can
    have a background color for the selected state and another for the
    unselected one. Similarly, the foreground color (usually the color
    used for the text on the component) can be individually defined for
    the two states.

  • Text fonts: Text can be rendered using the standard
    font styles as supported by the platform, as well as bitmap fonts.
    The font for each component can be set through its style
    object.

  • Background transparency: The transparency of a
    component's background can be set to vary from fully opaque (the
    default setting) to fully transparent. The integer value 0
    corresponds to full transparency and 255 to complete opacity.

  • Background image: By default, the background of a
    component does not have any image. However, this setting can be
    used to specify an image to be used as the background.

  • Margin and padding: The visual layout of a component
    (derived from the "http://www.w3.org/TR/REC-CSS2/box.html">CSS Box Model) defines
    margin and padding. Figure 1 shows the meaning of the terms
    margin and padding in the context of LWUIT. Note that
    the content area is used for displaying the basic content
    such as text or image. Style allows margin and
    padding for each of the four directions (top, bottom, left,
    and right) to be individually set.

    Component layout
    Figure 1. Component layout

  • Background painters:
    Special-purpose painter objects can be used to customize the
    background of one or a group of components.

The Style class represents the collection of all
these attributes for 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 the style
object associated with a given component is altered.

When a component is created, a default Style object
gets associated with it. For any non-trivial application, the
visual attributes will need to be modified. One way of doing this
is to use the individual setter methods. "setter">If, for instance, the foreground color of a component
(thiscomponent) has to be changed to red, the following code
can be used:

[prettify]
        thiscomponent.getStyle().setFgColor(0xff0000);
[/prettify]

The second way to modify the settings of the default style is to
create a new style and hook it up to the relevant component. The
Style class has constructors that allow most of the
attributes to be specified. The following code snippet sets a new
style for a component:

[prettify]
        Font font = Font.createSystemFont
                (Font.FACE_SYSTEM,Font.STYLE_BOLD,Font.SIZE_LARGE);
        byte tr = (byte)255;
        Style newstyle = new Style
                (0x00ff00, 0x000000, 0xff0000, 0x4b338c, font, tr);
        thiscomponent.setStyle(newstyle);
[/prettify]

This code sets new foreground and background colors, font for
text, and background transparency. The constructor used here has
the form:

[prettify]
        Style(int fgColor, int bgColor, int fgSelectionColor,
                int bgSelectionColor, Font f, byte transparency)
[/prettify]

There is another form of this constructor that allows the image
to be set, in addition to the above attributes. The attributes not
supported by the constructor will, however, need to be set through
the respective setter methods.

Finally, visual attributes can also be set for an entire class
of components (say, for all labels in an application) by using a
theme, as we shall see a little later.

Using Style

We shall now build a simple display and see how style can be
used to specify the appearance of a component. Our application will
have a single form with a combo box and will look like the Figure
2:

A simple ComboBox

Figure 2. A simple combo box

All the attributes of the combo box shown here have default
values. The only exception is the foreground selection color, which
had to be changed to improve the visibility of the selected item.
Similarly, the form containing the combo box has just one modified
attribute -- its background color. The following code shows how
the form is created:

[prettify]
        .
        .
        .
        //create a form and set its title
        Form f = new Form("Simple ComboBox");

        //set layout manager for the form
        //f.setLayout(new FlowLayout());

        //set form background colour
        f.getStyle().setBgColor(0xd5fff9);
        .
        .
        .
[/prettify]

The first two lines of code are quite self-explanatory and
should be familiar to AWT/Swing developers. The third line sets the
background color attribute for the form.

The combo box is also instantiated in a similar manner:

[prettify]
        // Create a set of items
        String[] items = { "Red", "Blue", "Green", "Yellow" };
                
        //create a combobox with String[] items
        ComboBox combobox = new ComboBox(items);
[/prettify]

ComboBox is a subclass of List and
needs a supporting data structure. Here we use a string array to
represent this data structure.

Once we have our combo box ready, we would like to change its
foreground selection color to improve readability. So we write a
line of code just as we had done for the form:

[prettify]
        combobox.getStyle().setFgSelectionColor(0x0000ff);
[/prettify]

However, when we compile the code and run it, the result turns
out to be rather surprising -- the foreground color remains
unchanged! It works for the form, so why doesn't it work for a combo
box? To answer that question we need to keep in mind the basic
architecture of LWUIT. Like "http://java.sun.com/products/jfc/tsc/articles/architecture/">Swing,
LWUIT is designed around the MVC concept
. So the entity that
renders a component is logically separate from the component
itself. Also, the rendering object for a combo box (among others)
needs to be a subclass of Component, which means it
will have its own Style. Every combo box is created
with its default renderer, which is an instance of
DefaultListCellRenderer. When a combo box is drawn,
the style used is that belonging to the renderer and that is why
setting the foreground selection color in the Style
object for the combo box does not work. To make the setting
effective we have to modify the Style object for the
renderer:

[prettify]
        //set foreground selection colour for
        //the default combobox renderer
        //this will work
        DefaultListCellRenderer dlcr = 
                (DefaultListCellRenderer)combobox.getRenderer();
        dlcr.getStyle().setFgSelectionColor(0x0000ff);
[/prettify]

This time, when the code is compiled, it works.

Theme

In the preceding section, we saw how to set individual visual
attributes for a component. In an application with a large number
of UI components, setting attributes for each component can be a
tedious task and can also lead to errors. A Theme
allows us to set, in a single place, the attributes for an entire
class of components. This not only simplifies the task of setting
attributes for all components of a particular type, but also ensures
that any newly added component will look just like all the others
of the same type in the application. A Theme thereby
establishes a visual coherence through all the screens of an
application.

A Theme is a list of key-value pairs with an
attribute being a key and its value being the corresponding value.
An entry in the list might look like this:

[prettify]
        Form.bgColor= 555555
[/prettify]

This entry specifies that the background color of all forms in
the application will be (hex) 555555 in the RGB format. A theme is
packaged into a resource file that can also hold other items
like images and bitmaps for fonts. The LWUIT download bundle
includes a resource editor that offers a simple way to
create a theme and package it into a resource file. The editor is
available in the util directory of the bundle.
Launch it by double-clicking on
the icon, and the editor will open as shown below. The Resource
Editor
is also integrated into the Sprint WTK 3.3.1 and can be
accessed by selecting File -> Utilities -> LWUIT Resource Editor,
as seen in Figure 3.

The Resource Editor

Figure 3. The Resource Editor

To create a new theme, click the + button on
the left pane and a dialog for entering the name of the theme will
open. This is shown in Figure 4.

Creating a new theme

Figure 4. Creating a new theme

When you click OK, the name of the new theme
appears on the left pane. Click this theme label to get a blank
theme on the right pane, as seen in Figure 5.

The blank theme

Figure 5. The blank theme

To populate the blank theme, click the Add
button and the Add dialog will open. You can select a
component and an attribute from the top combo boxes on this dialog.
In Figure 6, the component selected is a form and the attribute
selected is background color. The RGB value of the color can
entered as a hex string in the space provided. You can also click
on the colored box next to the space to entering the color value.
This will open a color chooser, from which the value of the
selected color will be directly entered into the dialog.

<br "Adding an entry to the theme" />

Figure 6. Adding an entry to the theme

Click the OK button and the entry will appear
on the right panel of the main editor window. Note that entries can
be edited or removed by using the appropriate button. Once all
entries have been made, you can save it by selecting File -> Save
As. If you are using the Sprint WTK, then the resource file for an
application has to be in its res folder.

Now that we have seen how to create a theme, let us look at a
demo that illustrates its use. Our demo for this section also will
have combo boxes but will look a little more polished than the one
we have already seen. Figure 7 shows this demo screen. Note that
now the form has a background image and the combo boxes are built
around check boxes. Also, the title bar (at the top of the form) and
the menu bar (at the bottom) have background colors different from
the default (white).

<br "Demo screen with two combo boxes" />

Figure 7. Demo screen with two
combo boxes

Before looking at the theme that is responsible for this
difference in appearance, let us quickly check out the code used to
make the screen.

[prettify]
        //initialise the LWUIT Display
        //and register this MIDlet
        Display.init(this);

        try
      {
                //open the resource file
                //get and set the theme
                Resources r = Resources.open("/SDTheme1.res");
                UIManager.getInstance().
                        setThemeProps(r.getTheme("SDTheme1"));
      }
      catch (java.io.IOException e)
      {
                //if there is a problem print a message on console
                //in this case default settings will be used
                System.out.println
                        ("Unable to get Theme " + e.getMessage());
      }

        //create a form and set its title
        Form f = new Form("ComboBox Example");

        //set layout manager for the form
        f.setLayout(new FlowLayout());

        //create two sets of items
        String[] items = { "Red", "Blue", "Green", "Yellow" };
        String[] items2 = 
                {"Sky", "Field", "Ocean", "Hill", "Meadow"};

        //create two comboboxes with these items
        ComboBox comboBox = new ComboBox(items);
        ComboBox comboBox2 = new ComboBox(items2);

        //create new instances of CbPainter
        //and set them to combo boxes
        //so that a checkbox will be 
        //the basic building block
        CbPainter cbp = new CbPainter();
        comboBox.setListCellRenderer(cbp);

        CbPainter cbp2 = new CbPainter();
        comboBox2.setListCellRenderer(cbp2);

        //add the two combo boxes to the form
        f.addComponent(comboBox);
        f.addComponent(comboBox2);

        //create an "Exit" command and add it to the form
        f.addCommand(new Command("Exit"));

        //set this form as the listener for the command
        f.setCommandListener(this);

        //show this form
        f.show();
[/prettify]
confusing. if all components should have this method called,
then who calls it? does UIManager.setThemeProps() call it
on all existing components, or do I have to keep references
to all my components and call refreshTheme() on each one
or what? ca -->

Right at the beginning we see how to extract the theme from a
resource file. The theme is then set for the UIManager
instance. Here we have installed the theme at the start. But when a
theme is set on the fly, some of the components of the form on
screen may not be visible and the effect of setting a theme on
these components is not predictable. To make sure that even the
components that are not visible have their styles properly updated,
you should call the refreshTheme method:

[prettify]
        Display.getInstance().getCurrent().refreshTheme();
[/prettify]

The form and the combo boxes are created just as in our example
in the preceding section. There is no
code that adds visual gloss to this demo, as all attributes are
specified in a Theme. What is different here is that
instead of letting the combo boxes be drawn by the default
renderer, we have set our own renderers. This is shown by the
highlighted part of the code. These custom renderers make the combo
boxes look different.

The renderer itself is very simple. All it has to do is
implement the methods specified in the interface
ListCellRenderer. As we want our combo box to encapsulate a
checkbox, the renderer extends CheckBox. The
drawComboBox method of the DefaultLookAndFeel
class uses this renderer to get the component to be used for
drawing the combo box. In this case the component so obtained is a
checkbox, as we see from the code below.

[prettify]
//objects of this class will be used to paint the combo boxes
class CbPainter extends CheckBox implements ListCellRenderer
{
        public CbPainter()
        {
                super("");
        }

        //returns a properly initialised component
        //that is ready to draw the checkbox
        public Component getListCellRendererComponent
                (List list,Object value,int index,boolean isSelected)
        {
                setText("" + value);
                if (isSelected)
                {
                        setFocus(true);
                        setSelected(true);
                }
                else
                {
                        setFocus(false);
                        setSelected(false);
                }

                return this;
        }

        //returns the component required for drawing
        //the focussed item
        public Component getListFocusComponent(List list)
        {
                setText("");
                setFocus(true);
                setSelected(true);

                return this;
        }
}
[/prettify]

It is not necessary that a combo box should look only like a
plain list or a checkbox. It can be built around some other
standard component or even around a totally new component with its
own unique look. Figure 8 shows a combo box that has a radio button
as its renderer.

<br "ComboBox with a radio button renderer" />

Figure 8. ComboBox with a radio button renderer


To see the theme that defines the look of our demo, you
will need the Resource Editor on your computer. "#launch">Launch either the Resource Editor that comes with the
LWUIT download or the one integrated into the Sprint Toolkit. Once
the Resource Editor opens, select File -> Open to
locate and open the resource file. The Resource Editor will show
SDTheme1 on the left panel under Themes.
Clicking SDTheme1 will display the details of the theme
on the right panel as shown in Figure 9.

The theme for the demo

Figure 9. The theme for the demo

The first point to note is that there is one entry at the bottom
that appears in bold letters. All such entries are the defaults.
In our example, the only component-specific font setting is for the
soft button -- the Exit button at left bottom corner.
The fonts for the form title and the combo box
string are not defined. These fonts will be rendered as per the
default setting.

In our earlier example, we saw that the selection color for the
text had to be set in the renderer. In the present example, we know
the rendering is actually being done by a checkbox renderer. So the
background and foreground colors have been defined for checkboxes
and, indeed, the colors for rendering the text and the text
background (both for the focussed and non-focussed states) are as
per these definitions. This can be seen in Figure 10.

<br "Foreground and background colours" />

Figure 10. Foreground and background colors

In the figure above we can also see the effect of checkbox
transparency value of 127 (semi-transparent). The three unselected
entries in the drop-down list have a dark tint because of this
transparency setting. You can experiment with this value to see how
these backgrounds change. Incidentally, when you make a change in
the theme, it is not necessary to rebuild the application. Just
save the resource file and click Run.

When a new theme is installed, all applicable styles are updated
except those attributes that have been manually altered by using
one of the accessor methods of the Style class
discussed earlier. However, if you want the
new theme to be effective even for the attributes that have been
manually changed, then use one of the setters in Style
that take two arguments, the second one being a Boolean variable.
For example:

[prettify]
        setFgColor(int fgColor, boolean override);
[/prettify]

If the Boolean argument is set to true when an attribute
is manually changed, then the values specified in the new theme will
override the manually set value too. The code will look like
this:

[prettify]
        thiscomponent.getStyle().setFgColor(0xff0000, true);
[/prettify]

Painter

The Painter interface allows the background of a
component to look the way you want. Recall our discussion on
style where we had seen that one of the attributes was "#bgp">background painter. In this section we shall see how a
simple background painter can be used.

Referring to our demo screenshot, the
color of the background on which the text has been drawn cannot be
changed through style or theme. The reason for this becomes clear
when we analyze the structure of a combo box, as shown in Figure 11,
and the sequence of rendering it.

<br "Structure of our combo box" />

Figure 11. Structure of our combo box

When a combo box needs to be redrawn (say, because it has just
received focus), the following sequence of events takes place.

  1. The obsolete combo box is deleted. This is done by drawing a
    filled rectangle of the same size and with a transparency of 0
    (fully transparent).
  2. Then the checkbox selection and the text are drawn.
  3. Next the combo button is drawn.
  4. And finally, the combo border is drawn.

We see now that the combo background is not redrawn after the
first step. So this part remains a fully transparent layer and it
is the form background that shows through. You can change the form
background color in the theme and you will see that this color
also becomes the combo background color.

If we now want to have a different color (or a pattern or an
image) on the combo background, we need to use a
Painter. We shall see what a simple painter looks like
and how to use it.

narrower than this
12345678901234567890123456789012345678901234567890123456789012345
-->

[prettify]
public class ComboBgPainter implements Painter
{
        private int bgcolor;

        public ComboBgPainter(int bgcolor)
        {
                this.bgcolor = bgcolor;
        }

        public void paint(Graphics g, Rectangle rect)
        {
                //probably redundant 
                //but save the current colour anyway
                int color = g.getColor();

                //set the given colour
                g.setColor(bgcolor);

                //get the position and dimension 
                //of rectangle to be drawn
                int x = rect.getX();
                int y = rect.getY();
                int wd = rect.getSize().getWidth();
                int ht = rect.getSize().getHeight();

                //draw the filled rectangle
                g.fillRect(x, y, wd, ht);

                //restore colour setting
                g.setColor(color);
        }
}
[/prettify]

The code is simple enough -- all it does is draw a filled
rectangle using the color passed to the constructor. The rectangle
is drawn at the position and with the dimensions defined by
rect.

What we now have to do is hook up the painter to the combo box
that needs to have its background painted. We do it by adding the
highlighted line after the code instantiating the two combo boxes.
Note that only one combo box will have its background painted.

[prettify]
                //create two comboboxes with these items
                ComboBox combobox = new ComboBox(items);
                ComboBox combobox2 = new ComboBox(items2);

                //set the painter 
                combobox.getStyle().setBgPainter
                        (new ComboBgPainter(0x4b338c));
[/prettify]

Figure 12 shows that the background of the combo box on the left
has been painted as expected. If we had wanted to paint the
background of the other combo box too, we would have used the same
painter. As a matter of fact, we could create an instance of the
painter and set the same instance on all combo boxes.

<br "Combo box with painted background" />

Figure 12. Combo box with painted background

Conclusion

We have seen how we can use Style,
Theme, and Painter to create a set of
visually attractive and uniform components with the LWUIT platform.
Recently LWUIT has been open sourced. A detailed study of the
source code is a very fascinating experience and will develop the
kind of insight required for proper utilization of this library and
also for interesting experimentation.

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.
Related Topics >> GUI   |   Mobility   |   Programming   |