This chapter covers the components of Codename One. Not all components are covered, but it tries to go deeper than the JavaDocs.

Container

The Codename One container is a base class for many high level components; a container is a component that can contain other components.

Every component has a parent container that can be null if it isn’t within a container at the moment or is a top-level container. A container can have many children.

Component-Container relationship expressed as UML
Figure 1. Component-Container relationship expressed as UML

Components are arranged in containers using layout managers which are algorithms that determine the arrangement of components within the container.

You can read more about layout managers in the basics section.

Composite Components

Codename One components share a very generic hierarchy of inheritance e.g. Button derives from Label and thus receives all its abilities.

However, some components are composites and derive from the Container class. E.g. the MultiButton is a composite button that derives from Container but acts/looks like a Button. Normally this is pretty seamless for the developer, with a few things to keep in mind.

  • You should not use the Container derived methods on such a composite component (e.g. add/remove etc.).

  • You can’t cast it to the type that it relates to e.g. you can’t cast MultiButton to Button.

  • Events might be more nuanced. E.g. if you rely on ActionEvent.getSource() or ActionEvent.getComponent() notice that they might not behave the way you would expect. For a MultiButton they will return the underlying Button. To workaround this we have ActionEvent.getActualComponent().

Lead Component

Codename One has a rather unique feature for creating composite components: "lead components". This feature effectively allows components like MultiButton to act as if they are a single component while really being comprised of multiple components.

Lead components work by setting a single component within as the "leader" it determines the style state for all the components in the hierarchy so if we have a Container that is lead by a Button the button will determine if the selected/pressed state is returned for the entire container hierarchy.

This creates a case where a single Component has multiple nested UIID’s e.g. `MultiButton has UIID’s such as `MultiLine1 that can be customized via API’s such as setUIIDLine1.

The lead component also handles the events from a single source so clicking in one of the other components within the hierarchy will send the event to the leading Button resulting in action events that behave "oddly" (hence the need for getActualComponent);

You can learn more about lead components in here.

Form

Form is the top-level container of Codename One, Form derives from Container and is the element we “show”. Only one form can be visible at any given time. We can get the currently visible Form using the code:

Form currentForm = Display.getInstance().getCurrent();

A form is a unique container in the sense that it has a title, a content area and optionally a menu/menu bar area. When invoking methods such as add/remove on a form, you are in fact invoking something that maps to this:

myForm.getContentPane().add(...);

Form is in effect just a Container that has a border layout, its north section is occupied by the title area and its south section by the optional menu bar. The center (which stretches) is the content pane. The content pane is where you place all your components.

Form layout graphic
Figure 2. Form layout graphic

You can see that every Form has space allocated for the title area. If you don’t set the title it won’t show up (its size will be zero), but it will still be there. The same isn’t always true for the case of the menu bar, which can vary significantly. Effectively, the section that matters is the content pane, so the form tries to do the “right thing” by pretending to be the content pane. However, this isn’t always seamless and sometimes code needs to just invoke getContentPane() in order to work directly with the container.

A good example for such a case is with layout animations. Animating the form might not produce the right results. When in doubt its pretty easy to just use getContentPane instead of working with the Form directly.

As you can see from the graphic, Form has two layers that reside on top of the content pane/title. The first is the layered pane which allows you to place "always on top" components. The layered pane added implicitly when you invoke getLayeredPane().

You still need to place components using layout managers in order to get them to appear in the right place when using the layered pane.

The second layer is the glass pane which allows you to draw arbitrary things on top of everything. The order in the image is indeed accurate:

  1. ContentPane is lowest

  2. LayeredPane is second

  3. GlassPane is painted last

Its important to notice that a layered pane is on top of the ContentPane only and doesn’t stretch to the title. A GlassPane usually stretches all the way but only with a "lightweight" title area e.g. the Toolbar API.

The GlassPane allows developers to overlay UI on top of existing UI and paint as they see fit. This is useful for things that provide notification but don’t want to intrude with application functionality.

Both LayeredPane & GlassPane won’t work with "native" peer components such as media, browser, native maps etc.

Dialog

A Dialog is a special kind of Form that can occupy only a portion of the screen, it also has the additional functionality of the modal show method.

When showing a dialog we have two basic options: modeless and modal:

  • Modal dialogs (the default) block the current EDT thread until the dialog is dismissed (to understand how they do it, read about invokeAndBlock).
    Modal dialogs are an extremely useful way to prompt the user since the code can assume the user responded in the next line of execution. This promotes a linear & intuitive way of writing code.

  • Modless dialogs return immediately so a call to show such a dialog can’t assume anything in the next line of execution. This is useful for features such as progress indicators where we aren’t waiting for user input.

E.g. a modal dialog can be expressed as such:

if(Dialog.show("Click Yes Or No", "Select one", "Yes", "No")) {
    // user clicked yes
} else {
    // user clicked no
}

Notice that during the show call above the execution of the next line was "paused" until we got a response from the user and once the response was returned we could proceed directly.

All usage of Dialog must be within the Event Dispatch Thread (the default thread of Codename One). This is especially true for modal dialogs. The Dialog class knows how to "block the EDT" without blocking it.

To learn more about invokeAndBlock which is the workhorse behind the modal dialog functionality check out the EDT section.

The Dialog class contains multiple static helper methods to quickly show user notifications, but also allows a developer to create a Dialog instance, add information to its content pane and show the dialog.

Dialogs contain a ContentPane just like Form.

When showing a dialog in this way, you can either ask Codename One to position the dialog in a specific general location (taken from the BorderLayout concept for locations) or position it by spacing it (in pixels) from the 4 edges of the screen.

E.g. you could do something like this to show a simple modal Dialog:

Dialog d = new Dialog("Title");
d.setLayout(new BorderLayout());
d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody"));
d.showPacked(BorderLayout.SOUTH, true);
Custom Dialog in the south position
Figure 3. Custom modal Dialog in the south position
You can turn the code above to a modless Dialog by flipping the boolean true argument to false.

We can position a Dialog absolutely by determining the space from the edges e.g. with this code we can occupy the bottom portion of the screen:

Dialog d = new Dialog("Title");
d.setLayout(new BorderLayout());
d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody"));
d.show(hi.getHeight() / 2, 0, 0, 0);
Custom Dialog positioned absolutely
Figure 4. Custom Dialog positioned absolutely
hi is the name of the parent Form in the sample above.

Styling Dialogs

It’s important to style a Dialog using getDialogStyle() or setDialogUIID methods rather than styling the dialog object directly.

The reason for this is that the Dialog is really a Form that takes up the whole screen. The Form that is visible behind the Dialog is rendered as a screenshot. So customizing the actual UIID of the Dialog won’t produce the desired results.

Tint & Blurring

By default a Dialog uses a platform specific tint color when it is showing e.g. notice the background in the image below is tinted:

Form hi = new Form("Tint Dialog", new BoxLayout(BoxLayout.Y_AXIS));
Button showDialog = new Button("Tint");
showDialog.addActionListener((e) -> Dialog.show("Tint", "Is On....", "OK", null));
hi.add(showDialog);
hi.show();
Dialog with tinted background
Figure 5. Dialog with tinted background

The tint color can be manipulated on the parent form, you can set it to any AARRGGBB value to set any color using the setTintColor method. Notice that this is invoked on the parent form and not on the Dialog!

This is an AARRGGBB value and not an RRGGBB value! This means that 0 will be transparent.

You can also manipulate this default value globally using the theme constant tintColor. The sample below tints the background in green:

Form hi = new Form("Tint Dialog", new BoxLayout(BoxLayout.Y_AXIS));
hi.setTintColor(0x7700ff00);
Button showDialog = new Button("Tint");
showDialog.addActionListener((e) -> Dialog.show("Tint", "Is On....", "OK", null));
hi.add(showDialog);
hi.show();
Dialog with green tinted background
Figure 6. Dialog with green tinted background

We can apply Gaussian blur to the background of a dialog to highlight the foreground further and produce a very attractive effect. We can use the setDefaultBlurBackgroundRadius to apply this globally, we can use the theme constant dialogBlurRadiusInt to do the same or we can do this on a per Dialog basis using setBlurBackgroundRadius.

Not all device types support blur you can test if your device supports it using Display.getInstnace().isGaussianBlurSupported(). If blur isn’t supported the blur setting will be ignored.
Form hi = new Form("Blur Dialog", new BoxLayout(BoxLayout.Y_AXIS));
Dialog.setDefaultBlurBackgroundRadius(8);
Button showDialog = new Button("Blur");
showDialog.addActionListener((e) -> Dialog.show("Blur", "Is On....", "OK", null));
hi.add(showDialog);
hi.show();
The blur effect coupled with the OS default tint
Figure 7. The blur effect coupled with the OS default tint

It might be a bit hard to notice the blur effect with the tinting so here is the same code with tinting disabled:

hi.setTintColor(0);
The blur effect is more pronounced when the tint is disabled
Figure 8. The blur effect is more pronounced when the tint is disabled

Popup Dialog

A popup dialog is a common mobile paradigm showing a Dialog that points at a specific component. It’s just a standard Dialog that is shown in a unique way:

Dialog d = new Dialog("Title");
d.setLayout(new BorderLayout());
d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody"));
d.showPopupDialog(showDialog);
Popup Dialog
Figure 9. Popup Dialog

The popup dialog accepts a Component or Rectangle to point at and handles the rest.

Styling The Arrow Of The Popup Dialog

One of the harder aspects of a popup dialog is the construction of the theme elements required for arrow styling. To get that sort of behavior you will need a custom image border and 4 arrows pointing in each direction that will be overlaid with the border.

The sizes of the arrow images should be similarly proportioned and fit within the image borders whitespace. The block image of the dialog should have empty pixels in the sides to reserve space for the arrow. E.g. if the arrows are all 32x32 pixels then the PopupDialog image should have 32 pixels of transparent pixels around it.

You will need to define the following theme constants for the arrow to work:

PopupDialogArrowBool=true
PopupDialogArrowTopImage=arrow up image
PopupDialogArrowBottomImage=arrow down image
PopupDialogArrowLeftImage=arrow left image
PopupDialogArrowRightImage=arrow right image

Then style the PopupDialog UIID with the image for the Dialog itself.

InteractionDialog

Dialogs in Codename One can be modal or modeless, the former blocks the calling thread and the latter does not. However, there is another definition to those terms: A modal dialog blocks access to the rest of the UI while a modeless dialog "floats" on top of the UI.

In that sense, all dialogs in Codename One are modal; they block the parent form since they are effectively just forms that show the "parent" in their background. InteractionDialog has an API that is very similar to the Dialog API but, unlike dialog, it never blocks anything. Neither the calling thread nor the UI.

InteractionDialog isn’t a Dialog since it doesn’t share the same inheritance hierarchy. However, it acts and "feels" like a Dialog despite the fact that it’s just a Container in the LayeredPane.

InteractionDialog is really just a container that is positioned within the layered pane. Notice that because of that design, you can have only one such dialog at the moment and, if you add something else to the layered pane, you might run into trouble.

Using the interaction dialog is pretty trivial and very similar to dialog:

InteractionDialog dlg = new InteractionDialog("Hello");
dlg.setLayout(new BorderLayout());
dlg.add(BorderLayout.CENTER, new Label("Hello Dialog"));
Button close = new Button("Close");
close.addActionListener((ee) -> dlg.dispose());
dlg.addComponent(BorderLayout.SOUTH, close);
Dimension pre = dlg.getContentPane().getPreferredSize();
dlg.show(0, 0, Display.getInstance().getDisplayWidth() - (pre.getWidth() + pre.getWidth() / 6), 0);
Interaction Dialog
Figure 10. Interaction Dialog

This will show the dialog on the right hand side of the screen, which is pretty useful for a floating in place dialog.

The InteractionDialog can only be shown at absolute or popup locations. This is inherent to its use case which is "non-blocking". When using this component you need to be very aware of its location.

Label

Label represents a text, icon or both. Label is also the base class of Button which in turn is the base class for RadioButton & CheckBox. Thus the functionality of the Label class extends to all of these components.

Label text can be positioned in one of 4 locations as such:

Label left = new Label("Left", icon);
left.setTextPosition(Component.LEFT);
Label right = new Label("Right", icon);
right.setTextPosition(Component.RIGHT);
Label bottom = new Label("Bottom", icon);
bottom.setTextPosition(Component.BOTTOM);
Label top = new Label("Top", icon);
top.setTextPosition(Component.TOP);
hi.add(left).add(right).add(bottom).add(top);
Label positions
Figure 11. Label positions

Label allows only a single line of text, line breaking is a very expensive operation on mobile devices [1] and so the Label class doesn’t support it.

SpanLabel supports multiple lines with a single label, notice that it does carry a performance penalty for this functionality.

Labels support tickering and the ability to end with “…​” if there isn’t enough space to render the label. Developers can determine the placement of the label relatively to its icon in quite a few powerful ways.

TextField & TextArea

The TextField class derives from the TextArea class, and both are used for text input in Codename One.

TextArea defaults to multi-line input and TextField defaults to single line input but both can be used in both cases. The main differences between TextField and TextArea are:

  • Blinking cursor is rendered on TextField only

  • DataChangeListener is only available in TextField. This is crucial for character by character input event tracking

  • Done listener is only available in the TextField

  • Different UIID

The semantic difference between TextField & TextArea dates back to the ancestor of Codename One: LWUIT. Feature phones don’t have “proper” in-place editing capabilities & thus TextField was introduced to allow such input.

Because it lacks the blinking cursor capability TextArea is often used as a multi-line label and is used internally in SpanLabel, SpanButton etc.

A common use case is to have an important text component in edit mode immediately as we enter a Form. Codename One forms support this exact use case thru the Form.setEditOnShow(TextArea) method.

TextField & TextArea support constraints for various types of input such as NUMERIC, EMAIL, URL, etc. Those usually affect the virtual keyboard used, but might not limit input in some platforms. E.g. on iOS even with NUMERIC constraint you would still be able to input characters.

If you need to prevent specific types of input check out the validation section.

The following sample shows off simple text field usage:

TableLayout tl;
int spanButton = 2;
if(Display.getInstance().isTablet()) {
    tl = new TableLayout(7, 2);
} else {
    tl = new TableLayout(14, 1);
    spanButton = 1;
}
tl.setGrowHorizontally(true);
hi.setLayout(tl);

TextField firstName = new TextField("", "First Name", 20, TextArea.ANY);
TextField surname = new TextField("", "Surname", 20, TextArea.ANY);
TextField email = new TextField("", "E-Mail", 20, TextArea.EMAILADDR);
TextField url = new TextField("", "URL", 20, TextArea.URL);
TextField phone = new TextField("", "Phone", 20, TextArea.PHONENUMBER);

TextField num1 = new TextField("", "1234", 4, TextArea.NUMERIC);
TextField num2 = new TextField("", "1234", 4, TextArea.NUMERIC);
TextField num3 = new TextField("", "1234", 4, TextArea.NUMERIC);
TextField num4 = new TextField("", "1234", 4, TextArea.NUMERIC);

Button submit = new Button("Submit");
TableLayout.Constraint cn = tl.createConstraint();
cn.setHorizontalSpan(spanButton);
cn.setHorizontalAlign(Component.RIGHT);
hi.add("First Name").add(firstName).
        add("Surname").add(surname).
        add("E-Mail").add(email).
        add("URL").add(url).
        add("Phone").add(phone).
        add("Credit Card").
                add(GridLayout.encloseIn(4, num1, num2, num3, num4)).
        add(cn, submit);
Simple text component sample
Figure 12. Simple text component sample
The Toolbar section contains a very elaborate TextField search sample with DataChangeListener and rather unique styling.

Masking

A common use case when working with text components is the ability to "mask" input e.g. in the credit card number above we would want 4 digits for each text field and don’t want the user to tap Next 3 times.

Masking allows us to accept partial input in one field and implicitly move to the next, this can be used to all types of complex input thanks to the text component API. E.g with the code above we can mask the credit card input so the cursor jumps to the next field implicitly using this code:

automoveToNext(num1, num2);
automoveToNext(num2, num3);
automoveToNext(num3, num4);

Then implement the method automoveToNext as:

private void automoveToNext(final TextField current, final TextField next) {
    current.addDataChangeListener((type, index) -> {
        if(current.getText().length() == 5) {
            current.stopEditing();
            String val = current.getText();
            current.setText(val.substring(0, 4));
            next.setText(val.substring(4));
            next.startEditing();
        }
    });
}

The Virtual Keyboard

A common misconception for developers is assuming the virtual keyboard represents "keys". E.g. developers often override the "keyEvent" callbacks which are invoked for physical keyboard typing and expect those to occur with a virtual keyboard.

This isn’t the case since a virtual keyboard is a very different beast. With a virtual keyboard characters typed might produce a completely different output due to autocorrect. Some keyboards don’t even have "keys" in the traditional sense or don’t type them in the traditional sense (e.g. swiping).

The constraint property for the TextField/TextArea is crucial for a virtual keyboard.
When working with a virtual keyboard it’s important that the parent Container for the TextField/TextArea is scrollable. Otherwise the component won’t be reachable or the UI might be distorted when the keyboard appears.
Action Button Client Property

By default, the virtual keyboard on Android has a "Done" button, you can customize it to be a search icon, a send icon, or a go icon using a hint such as this:

searchTextField.putClientProperty("searchField", Boolean.TRUE);
sendTextField.putClientProperty("sendButton", Boolean.TRUE);
goTextField.putClientProperty("goButton", Boolean.TRUE);

This will adapt the icon for the action on the keys.

Next & Done on iOS

We try to hide a lot of the platform differences in Codename One, input is very different between OS’s. A common reliance is the ability to send the "Done" event when the user presses the Done button. Unfortunately this button doesn’t always exist e.g. if there is an Enter button (due to multiline input) or if there is a Next button in that place.

To make the behavior more uniform we slightly customized the iOS keyboard as such:

Next virtual keyboard with toolbar
Figure 13. Next virtual keyboard with toolbar
Done virtual keyboard without toolbar
Figure 14. Done virtual keyboard without toolbar
This works with 3rd party keyboards too…​

However, this behavior might not be desired so to block that we can do:

tf.putClientProperty("iosHideToolbar", Boolean.TRUE);

This will hide the toolbar for that given field.

Button

Button is a subclass of Label and as a result it inherits all of its functionality, specifically icon placement, tickering, etc.

Button adds to the mix some additional states such as a pressed UIID state and pressed icon.

There are additional icon states in Button such as rollover and disabled icon.

Button also exposes some functionality for subclasses specifically the setToggle method call which has no meaning when invoked on a Button but has a lot of implications for CheckBox & RadioButton.

Button event handling can be performed via an ActionListener or via a Command.

Changes in a Command won’t be reflected into the Button after the command was set to the Button.

Here is a trivial hello world style Button:

Form hi = new Form("Button");
Button b = new Button("My Button");
hi.add(b);
b.addActionListener((e) -> Log.p("Clicked"));
Simple button in the iOS styling
Figure 15. Simple button in the iOS styling, notice iOS doesn’t have borders on buttons…​

Such a button can be styled to look like a link using code like this or simply by making these settings in the theme and using code such as btn.setUIID("Hyperlink").

Form hi = new Form("Button");
Button b = new Button("Link Button");
b.getAllStyles().setBorder(Border.createEmpty());
b.getAllStyles().setTextDecoration(Style.TEXT_DECORATION_UNDERLINE);
hi.add(b);
b.addActionListener((e) -> Log.p("Clicked"));
Button styled to look like a link
Figure 16. Button styled to look like a link

CheckBox/RadioButton

CheckBox & RadioButton are subclasses of button that allow for either a toggle state or exclusive selection state.

Both CheckBox & RadioButton have a selected state that allows us to determine their selection.

RadioButton doesn’t allow us to "deselect" it, the only way to "deselect" a RadioButton is by selecting another RadioButton.

The CheckBox can be added to a Container like any other Component but the RadioButton must be associated with a ButtonGroup otherwise if we have more than one set of RadioButton’s in the form we might have an issue.

Notice in the sample below that we associate all the radio buttons with a group but don’t do anything with the group as the radio buttons keep the reference internally. We also show the opposite side functionality and icon behavior:

CheckBox cb1 = new CheckBox("CheckBox No Icon");
cb1.setSelected(true);
CheckBox cb2 = new CheckBox("CheckBox With Icon", icon);
CheckBox cb3 = new CheckBox("CheckBox Opposite True", icon);
CheckBox cb4 = new CheckBox("CheckBox Opposite False", icon);
cb3.setOppositeSide(true);
cb4.setOppositeSide(false);
RadioButton rb1 = new RadioButton("Radio 1");
RadioButton rb2 = new RadioButton("Radio 2");
RadioButton rb3 = new RadioButton("Radio 3", icon);
new ButtonGroup(rb1, rb2, rb3);
rb2.setSelected(true);
hi.add(cb1).add(cb2).add(cb3).add(cb4).add(rb1).add(rb2).add(rb3);
RadioButton & CheckBox usage
Figure 17. RadioButton & CheckBox usage

Both of these components can be displayed as toggle buttons (see the toggle button section below), or just use the default check mark/filled circle appearance based on the type/OS.

Toggle Button

A toggle button is a button that is pressed and stays pressed. When a toggle button is pressed again it’s released from the pressed state. Hence the button has a selected state to indicate if it’s pressed or not exactly like the CheckBox/RadioButton components in Codename One.

To turn any CheckBox or RadioButton to a toggle button just use the setToggle(true) method. Alternatively you can use the static createToggle method on both CheckBox and RadioButton to create a toggle button directly.

Invoking setToggle(true) implicitly converts the UIID to ToggleButton unless it was changed by the user from its original default value.

We can easily convert the sample above to use toggle buttons as such:

CheckBox cb1 = CheckBox.createToggle("CheckBox No Icon");
cb1.setSelected(true);
CheckBox cb2 = CheckBox.createToggle("CheckBox With Icon", icon);
CheckBox cb3 = CheckBox.createToggle("CheckBox Opposite True", icon);
CheckBox cb4 = CheckBox.createToggle("CheckBox Opposite False", icon);
cb3.setOppositeSide(true);
cb4.setOppositeSide(false);
ButtonGroup bg = new ButtonGroup();
RadioButton rb1 = RadioButton.createToggle("Radio 1", bg);
RadioButton rb2 = RadioButton.createToggle("Radio 2", bg);
RadioButton rb3 = RadioButton.createToggle("Radio 3", icon, bg);
rb2.setSelected(true);
hi.add(cb1).add(cb2).add(cb3).add(cb4).add(rb1).add(rb2).add(rb3);
Toggle button converted sample
Figure 18. Toggle button converted sample

That’s half the story though, to get the full effect of some cool toggle button UI’s we can use a ComponentGroup. This allows us to create a button bar effect with the toggle buttons.

E.g. lets enclose the CheckBox components in a vertical ComponentGroup and the RadioButton’s in a horizontal group. We can do this by changing the last line of the code above as such:

hi.add(ComponentGroup.enclose(cb1, cb2, cb3, cb4)).
        add(ComponentGroup.encloseHorizontal(rb1, rb2, rb3));
Toggle button converted sample wrapped in ComponentGroup
Figure 19. Toggle button converted sample wrapped in ComponentGroup

ComponentGroup

ComponentGroup is a special container that can be either horizontal or vertical (BoxLayout X_AXIS or Y_AXIS respectively).

ComponentGroup "restyles" the elements within the group to have a UIID that allows us to create a "round border" effect that groups elements together.

The following code adds 4 component groups to a Container to demonstrate the various UIID changes:

hi.add("Three Labels").
        add(ComponentGroup.enclose(new Label("GroupElementFirst UIID"), new Label("GroupElement UIID"), new Label("GroupElementLast UIID"))).
        add("One Label").
        add(ComponentGroup.enclose(new Label("GroupElementOnly UIID"))).
        add("Three Buttons").
        add(ComponentGroup.enclose(new Button("ButtonGroupFirst UIID"), new Button("ButtonGroup UIID"), new Button("ButtonGroupLast UIID"))).
        add("One Button").
        add(ComponentGroup.enclose(new Button("ButtonGroupOnly UIID")));
ComponentGroup adapts the UIID's of the components added so we can style them
Figure 20. ComponentGroup adapts the UIID’s of the components added so we can style them

Notice the following about the code above and the resulting image:

  • Buttons have a different UIID than other element types. Their styling is slightly different in such UI’s so you need to pay attention to that.

  • When an element is placed alone within a ComponentGroup its a special case UIID.

By default, ComponentGroup does nothing. You need to explicitly activate it in the theme by setting a theme property to true. Specifically you need to set ComponentGroupBool to true for ComponentGroup to do something otherwise its just a box layout container! The ComponentGroupBool flag is true by default in the iOS native theme.

When ComponentGroupBool is set to true, the component group will modify the styles of all components placed within it to match the element UIID given to it (GroupElement by default) with special caveats to the first/last/only elements. E.g.

  1. If I have one element within a component group it will have the UIID: GroupElementOnly

  2. If I have two elements within a component group they will have the UIID’s GroupElementFirst, GroupElementLast

  3. If I have three elements within a component group they will have the UIID’s GroupElementFirst, GroupElement, GroupElementLast

  4. If I have four elements within a component group they will have the UIID’s GroupElementFirst, GroupElement, GroupElement, GroupElementLast

This allows you to define special styles for the edges.

You can customize the UIID set by the component group by calling setElementUIID in the component group e.g. setElementUIID("ToggleButton") for three elements result in the following UIID’s:

ToggleButtonFirst, ToggleButton, ToggleButtonLast

MultiButton

MultiButton is a composite component (lead component) that acts like a versatile Button. It supports up to 4 lines of text (it doesn’t automatically wrap the text), an emblem (usually navigational arrow, or check box) and an icon.

MultiButton can be used as a button, a CheckBox or a RadioButton for creating rich UI’s.

The MultiButton was inspired by the aesthetics of the UITableView iOS component.

A common source of confusion in the MultiButton is the difference between the icon and the emblem, since both may have an icon image associated with them. The icon is an image representing the entry while the emblem is an optional visual representation of the action that will be undertaken when the element is pressed. Both may be used simultaneously or individually of one another.

MultiButton twoLinesNoIcon = new MultiButton("MultiButton");
twoLinesNoIcon.setTextLine2("Line 2");
MultiButton oneLineIconEmblem = new MultiButton("Icon + Emblem");
oneLineIconEmblem.setIcon(icon);
oneLineIconEmblem.setEmblem(emblem);
MultiButton twoLinesIconEmblem = new MultiButton("Icon + Emblem");
twoLinesIconEmblem.setIcon(icon);
twoLinesIconEmblem.setEmblem(emblem);
twoLinesIconEmblem.setTextLine2("Line 2");

MultiButton twoLinesIconEmblemHorizontal = new MultiButton("Icon + Emblem");
twoLinesIconEmblemHorizontal.setIcon(icon);
twoLinesIconEmblemHorizontal.setEmblem(emblem);
twoLinesIconEmblemHorizontal.setTextLine2("Line 2 Horizontal");
twoLinesIconEmblemHorizontal.setHorizontalLayout(true);

MultiButton twoLinesIconCheckBox = new MultiButton("CheckBox");
twoLinesIconCheckBox.setIcon(icon);
twoLinesIconCheckBox.setCheckBox(true);
twoLinesIconCheckBox.setTextLine2("Line 2");

MultiButton fourLinesIcon = new MultiButton("With Icon");
fourLinesIcon.setIcon(icon);
fourLinesIcon.setTextLine2("Line 2");
fourLinesIcon.setTextLine3("Line 3");
fourLinesIcon.setTextLine4("Line 4");

hi.add(oneLineIconEmblem).
        add(twoLinesNoIcon).
        add(twoLinesIconEmblem).
        add(twoLinesIconEmblemHorizontal).
        add(twoLinesIconCheckBox).
        add(fourLinesIcon);
Multiple usage scenarios for the MultiButton
Figure 21. Multiple usage scenarios for the MultiButton

Styling The MultiButton

Since the MultiButton is a composite component setting its UIID will only impact the top level UI.

To customize everything you need to customize the UIID’s for MultiLine1, MultiLine2, MultiLine3, MultiLine4 & Emblem.

You can customize the individual UIID’s thru the API directly using the setIconUIID, setUIIDLine1, setUIIDLine2, setUIIDLine3, setUIIDLine4 & setEmblemUIID.

SpanButton

SpanButton is a composite component (lead component) that looks/acts like a Button but can break lines rather than crop them when the text is very long.

Unlike the MultiButton it uses the TextArea internally to break lines seamlessly. The SpanButton is far simpler than the MultiButton and as a result isn’t as configurable.

SpanButton sb = new SpanButton("SpanButton is a composite component (lead component) that looks/acts like a Button but can break lines rather than crop them when the text is very long.");
sb.setIcon(icon);
hi.add(sb);
The SpanButton Component
Figure 22. The SpanButton Component
SpanButton is slower than both Button and MultiButton. We recommend using it only when there is a genuine need for its functionality.

SpanLabel

SpanLabel is a composite component (lead component) that looks/acts like a Label but can break lines rather than crop them when the text is very long.

SpanLabel uses the TextArea internally to break lines seamlessly and so doesn’t provide all the elaborate configuration options of Label.

One of the features of label that moved into SpanLabel to some extent is the ability to position the icon. However, unlike a Label the icon position is determined by the layout manager of the composite so setIconPosition accepts a BorderLayout constraint.

SpanLabel d = new SpanLabel("Default SpanLabel that can seamlessly line break when the text is really long.");
d.setIcon(icon);
SpanLabel l = new SpanLabel("NORTH Positioned Icon SpanLabel that can seamlessly line break when the text is really long.");
l.setIcon(icon);
l.setIconPosition(BorderLayout.NORTH);
SpanLabel r = new SpanLabel("SOUTH Positioned Icon SpanLabel that can seamlessly line break when the text is really long.");
r.setIcon(icon);
r.setIconPosition(BorderLayout.SOUTH);
SpanLabel c = new SpanLabel("EAST Positioned Icon SpanLabel that can seamlessly line break when the text is really long.");
c.setIcon(icon);
c.setIconPosition(BorderLayout.EAST);
hi.add(d).add(l).add(r).add(c);
The SpanLabel Component
Figure 23. The SpanLabel Component
SpanLabel is significantly slower than Label. We recommend using it only when there is a genuine need for its functionality.

OnOffSwitch

The OnOffSwitch allows you to write an application where the user can swipe a switch between two states (on/off). This is a common UI paradigm in Android and iOS, although it’s implemented in a radically different way in both platforms.

This is a rather elaborate component because of its very unique design on iOS, but we we’re able to accommodate most of the small behaviors of the component into our version, and it seamlessly adapts between the Android style and the iOS style.

The image below was generated based on the default use of the OnOffSwitch:

OnOffSwitch onOff = new OnOffSwitch();
hi.add(onOff);
The OnOffSwitch component as it appears on/off on iOS (top) and on Android (bottom)
Figure 24. The OnOffSwitch component as it appears on/off on iOS (top) and on Android (bottom)

As you can understand the difference between the way iOS and Android render this component has triggered two very different implementations within a single component. The Android implementation just uses standard buttons and is the default for non-iOS platforms.

You can force the Android or iOS mode by using the theme constant onOffIOSModeBool.

Validation

Validation is an inherent part of text input, and the Validator class allows just that. You can enable validation thru the Validator class to add constraints for a specific component. It’s also possible to define components that would be enabled/disabled based on validation state and the way in which validation errors are rendered (change the components UIID, paint an emblem on top, etc.). A Constraint is an interface that represents validation requirements. You can define a constraint in Java or use some of the builtin constraints such as LengthConstraint, RegexConstraint, etc.

This sample below continues from the place where the TextField sample above stopped by adding validation to that code.

Validator v = new Validator();
v.addConstraint(firstName, new LengthConstraint(2)).
        addConstraint(surname, new LengthConstraint(2)).
        addConstraint(url, RegexConstraint.validURL()).
        addConstraint(email, RegexConstraint.validEmail()).
        addConstraint(phone, new RegexConstraint(phoneRegex, "Must be valid phone number")).
        addConstraint(num1, new LengthConstraint(4)).
        addConstraint(num2, new LengthConstraint(4)).
        addConstraint(num3, new LengthConstraint(4)).
        addConstraint(num4, new LengthConstraint(4));

v.addSubmitButtons(submit);
Validation & Regular Expressions
Figure 25. Validation & Regular Expressions

InfiniteProgress

The InfiniteProgress indicator spins an image infinitely to indicate that a background process is still working.

This style of animation is often nicknamed "washing machine" as it spins endlessly.

InfiniteProgress can be used in one of two ways either by embedding the component into the UI thru something like this:

myContainer.add(new InfiniteProgress());

InfiniteProgress can also appear over the entire screen, thus blocking all input. This tints the background while the infinite progress rotates:

Dialog ip = new InfiniteProgress().showInifiniteBlocking();

// do some long operation here using invokeAndBlock or do something in a separate thread and callback later
// when you are done just call

ip.dispose();
Infinite progress
Figure 26. Infinite progress

The image used in the InfiniteProgress animation is defined by the native theme. You can override that definition either by defining the theme constant infiniteImage or by invoking the setAnimation method.

Despite the name of the method setAnimation expects a static image that will be rotated internally. Don’t use an animated image.

InfiniteScrollAdapter & InfiniteContainer

InfiniteScrollAdapter & InfiniteContainer allow us to create a scrolling effect that "never" ends with the typical Container/Component paradigm.

The motivation behind these classes is simple, say we have a lot of data to fetch from storage or from the internet. We can fetch the data in batches and show progress indication while we do this.

Infinite scroll fetches the next batch of data dynamically as we reach the end of the Container. InfiniteScrollAdapter & InfiniteContainer represent two similar ways to accomplish that task relatively easily.

Let start by exploring how we can achieve this UI that fetches data from a webservice:

InfiniteScrollAdapter demo code fetching property cross data
Figure 27. InfiniteScrollAdapter demo code fetching property cross data

The first step is creating the webservice call, we won’t go into too much detail here as webservices & IO are discussed later in the guide:

int pageNumber = 1;
java.util.List<Map<String, Object>> fetchPropertyData(String text) {
    try {
        ConnectionRequest r = new ConnectionRequest();
        r.setPost(false);
        r.setUrl("http://api.nestoria.co.uk/api");
        r.addArgument("pretty", "0");
        r.addArgument("action", "search_listings");
        r.addArgument("encoding", "json");
        r.addArgument("listing_type", "buy");
        r.addArgument("page", "" + pageNumber);
        pageNumber++;
        r.addArgument("country", "uk");
        r.addArgument("place_name", text);
        NetworkManager.getInstance().addToQueueAndWait(r);
        Map<String,Object> result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r.getResponseData()), "UTF-8"));
        Map<String, Object> response = (Map<String, Object>)result.get("response");
        return (java.util.List<Map<String, Object>>)response.get("listings");
    } catch(Exception err) {
        Log.e(err);
        return null;
    }
}
The demo code here doesn’t do any error handling! This is a very bad practice and it is taken here to keep the code short and readable. Proper error handling is used in the Property Cross demo.

The fetchPropertyData is a very simplistic tool that just fetches the next page of listings for the nestoria webservice. Notice that this method is synchronous and will block the calling thread (legally) until the network operation completes.

Now that we have a webservice lets proceed to create the UI. Check out the code annotations below:

Form hi = new Form("InfiniteScrollAdapter", new BoxLayout(BoxLayout.Y_AXIS));

Style s = UIManager.getInstance().getComponentStyle("MultiLine1");
FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 3), false); (1)

InfiniteScrollAdapter.createInfiniteScroll(hi.getContentPane(), () -> { (2)
    java.util.List<Map<String, Object>> data = fetchPropertyData("Leeds"); (3)
    MultiButton[] cmps = new MultiButton[data.size()];
    for(int iter = 0 ; iter < cmps.length ; iter++) {
        Map<String, Object> currentListing = data.get(iter);
        if(currentListing == null) { (4)
            InfiniteScrollAdapter.addMoreComponents(hi.getContentPane(), new Component[0], false);
            return;
        }
        String thumb_url = (String)currentListing.get("thumb_url");
        String guid = (String)currentListing.get("guid");
        String summary = (String)currentListing.get("summary");
        cmps[iter] = new MultiButton(summary);
        cmps[iter].setIcon(URLImage.createToStorage(placeholder, guid, thumb_url));
    }
    InfiniteScrollAdapter.addMoreComponents(hi.getContentPane(), cmps, true); (5)
}, true); (6)
1 Placeholder is essential for the URLImage class which we will discuss at a different place.
2 The InfiniteScrollAdapter accepts a runnable which is invoked every time we reach the edge of the scrolling. We used a closure instead of the typical run() method override.
3 This is a blocking call, after the method completes we’ll have all the data we need. Notice that this method doesn’t block the EDT illegally.
4 If there is no more data we call the addMoreComponents method with a false argument. This indicates that there is no additional data to fetch.
5 Here we add the actual components to the end of the form. Notice that we must not invoke the add/remove method of Container. Those might conflict with the work of the InfiniteScrollAdapter.
6 We pass true to indicate that the data isn’t "prefilled" so the method should be invoked immediately when the Form is first shown
Do not violate the EDT in the callback. It is invoked on the event dispatch thread and it is crucial

The InfiniteContainer

InfiniteContainer was introduced to simplify and remove some of the boilerplate of the InfiniteScrollAdapter. It takes a more traditional approach of inheriting the Container class to provide its functionality.

Unlike the InfiniteScrollAdapter the InfiniteContainer accepts an index and amount to fetch. This is useful for tracking your position but also important since the InfiniteContainer also implements Pull To Refresh as part of its functionality.

Converting the code above to an InfiniteContainer is pretty simple we just moved all the code into the callback fetchComponents method and returned the array of Component’s as a response.

Unlike the InfiniteScrollAdapter we can’t use the ContentPane directly so we have to use a BorderLayout and place the InfiniteContainer there:

Form hi = new Form("InfiniteContainer", new BorderLayout());

Style s = UIManager.getInstance().getComponentStyle("MultiLine1");
FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 3), false);

InfiniteContainer ic = new InfiniteContainer() {
    @Override
    public Component[] fetchComponents(int index, int amount) {
        java.util.List<Map<String, Object>> data = fetchPropertyData("Leeds");
        MultiButton[] cmps = new MultiButton[data.size()];
        for(int iter = 0 ; iter < cmps.length ; iter++) {
            Map<String, Object> currentListing = data.get(iter);
            if(currentListing == null) {
                return null;
            }
            String thumb_url = (String)currentListing.get("thumb_url");
            String guid = (String)currentListing.get("guid");
            String summary = (String)currentListing.get("summary");
            cmps[iter] = new MultiButton(summary);
            cmps[iter].setIcon(URLImage.createToStorage(placeholder, guid, thumb_url));
        }
        return cmps;
    }
};
hi.add(BorderLayout.CENTER, ic);

List, MultiList, Renderers & Models

InfiniteContainer/InfiniteScrollAdapter vs. List/ContainerList

Our recommendation is to always go with Container, InfiniteContainer or InfiniteScrollAdapter.

We recommend avoiding List or its subclasses/related classes specifically ContainerList & MultiList.

We recommend replacing ComboBox with Picker but that’s a completely different discussion.

A Container with ~5000 nested containers within it can perform on par with a List and probably exceed its performance when used correctly.

Larger sets of data are rarely manageable on phones or tablets so the benefits for lists are dubious.

In terms of API we found that even experienced developers experienced a great deal of pain when wrangling the Swing styled lists and their stateless approach.

Since animation, swiping and other capabilities that are so common in mobile are so hard to accomplish with lists we see no actual reason to use them.

Why Isn’t List Deprecated?

We deprecated ContainerList which performs really badly and has some inherent complexity issues. List has some unique use cases and is still used all over Codename One.

MultiList is a reasonable version of List that is far easier to use without most of the pains related to renderer configuration.

There are cases where using List or MultiList is justified, they are just rarer than usual hence our recommendation.

MVC In Lists

A Codename One List doesn’t contain components, but rather arbitrary data; this seems odd at first but makes sense. If you want a list to contain components, just use a Container.

The advantage of using a List in this way is that we can display it in many ways (e.g. fixed focus positions, horizontally, etc.), and that we can have more than a million entries without performance overhead. We can also do some pretty nifty things, like filtering the list on the fly or fetching it dynamically from the Internet as the user scrolls down the list. To achieve these things the list uses two interfaces: ListModel and ListCellRenderer. List model represents the data; its responsibility is to return the arbitrary object within the list at a given offset. Its second responsibility is to notify the list when the data changes, so the list can refresh.

Think of the model as an array of objects that can notify you when it changes.

The list renderer is like a rubber stamp that knows how to draw an object from the model, it’s called many times per entry in an animated list and must be very fast. Unlike standard Codename One components, it is only used to draw the entry in the model and is immediately discarded, hence it has no memory overhead, but if it takes too long to process a model value it can be a big bottleneck!

Think of the render as a translation layer that takes the "data" from the model and translates it to a visual representation.

This is all very generic, but a bit too much for most, doing a list "properly" requires some understanding. The main source of confusion for developers is the stateless nature of the list and the transfer of state to the model (e.g. a checkbox list needs to listen to action events on the list and update the model, in order for the renderer to display that state). Once you understand that it’s easy.

Understanding MVC

Let’s recap, what is MVC:

  • Model - Represents the data for the component (list), the model can tell us exactly how many items are in it and which item resides at a given offset within the model. This differs from a simple Vector (or array), since all access to the model is controlled (the interface is simpler), and unlike a Vector/Array, the model can notify us of changes that occur within it.

  • View - The view draws the content of the model. It is a "dumb" layer that has no notion of what is displayed and only knows how to draw. It tracks changes in the model (the model sends events) and redraws itself when it changes.

  • Controller - The controller accepts user input and performs changes to the model, which in turn cause the view to refresh.

Image by RegisFrey - Own work
Figure 28. Typical MVC Diagram [2]

Codename One’s List component uses the MVC paradigm in its implementation. List itself is the Controller (with a bit of the View mixed in). The ListCellRenderer interface is the rest of the View and the ListModel is (you guessed it by now) the Model.

When the list is painted, it iterates over the visible elements in the model and asks the model for the data, it then draws them using the renderer. Notice that because of this both the model and the renderer must be REALLY fast and that’s hard.

Why is this useful?

Since the model is a lightweight interface, it can be implemented by you and replaced in runtime if so desired, this allows several use cases:

  1. A list can contain thousands of entries but only load the portion visible to the user. Since the model will only be queried for the elements that are visible to the user, it won’t need to load the large data set into memory until the user starts scrolling down (at which point other elements may be offloaded from memory).

  2. A list can cache efficiently. E.g. a list can mirror data from the server into local RAM without actually downloading all the data. Data can also be mirrored from storage for better performance and discarded for better memory utilization.

  3. The is no need for state copying. Since renderers allow us to display any object type, the list model interface can be implemented by the application’s data structures (e.g. persistence/network engine), which would return internal application data structures saving you the need of copying application state into a list specific data structure. Note that this advantage only applies with a custom renderer which is pretty difficult to get right.

  4. Using the proxy pattern we can layer logic such as filtering, sorting, caching, etc. on top of existing models without changing the model source code.

  5. We can reuse generic models for several views, e.g. a model that fetches data from the server can be initialized with different arguments, to fetch different data for different views. View objects in different Forms can display the same model instance in different view instances, thus they would update automatically when we change one global model.

Most of these use cases work best for lists that grow to a larger size, or represent complex data, which is what the list object is designed to do.

Important - Lists & Layout Managers

Usually when working with lists, you want the list to handle the scrolling (otherwise it will perform badly). This means you should place the list in a non-scrollable container (no parent can be scrollable), notice that the content pane is scrollable by default, so you should disable that.

It is also recommended to place the list in the CENTER location of a BorderLayout to produce the most effective results. e.g.:

form.setScrollable(false);
form.setLayout(new BorderLayout());
form.add(BorderLayout.CENTER, myList);

MultiList & DefaultListModel

So after this long start lets show the first sample of creating a list using the MultiList.

The MultiList is a preconfigured list that contains a ready made renderer with defaults that make sense for the most common use cases. It still retains most of the power available to the List component but reduces the complexity of one of the hardest things to grasp for most developers: rendering.

The full power of the ListModel is still available and allows you to create a million entry list with just a few lines of code. However the objects that the model returns should always be in the form of Map objects and not an arbitrary object like the standard List allows.

Here is a simple example of a MultiList containing a highly popular subject matter:

Form hi = new Form("MultiList", new BorderLayout());

ArrayList<Map<String, Object>> data = new ArrayList<>();

data.add(createListEntry("A Game of Thrones", "1996"));
data.add(createListEntry("A Clash Of Kings", "1998"));
data.add(createListEntry("A Storm Of Swords", "2000"));
data.add(createListEntry("A Feast For Crows", "2005"));
data.add(createListEntry("A Dance With Dragons", "2011"));
data.add(createListEntry("The Winds of Winter", "2016 (please, please, please)"));
data.add(createListEntry("A Dream of Spring", "Ugh"));

DefaultListModel<Map<String, Object>> model = new DefaultListModel<>(data);
MultiList ml = new MultiList(model);
hi.add(BorderLayout.CENTER, ml);
Basic usage of the MultiList & DefaultListModel
Figure 29. Basic usage of the MultiList & DefaultListModel]

createListEntry is relatively trivial:

private Map<String, Object> createListEntry(String name, String date) {
    Map<String, Object> entry = new HashMap<>();
    entry.put("Line1", name);
    entry.put("Line2", date);
    return entry;
}

There is one major piece missing here and that is the cover images for the books. A simple approach would be to just place the image objects into the entries using the "icon" property as such:

private Map<String, Object> createListEntry(String name, String date, Image cover) {
    Map<String, Object> entry = new HashMap<>();
    entry.put("Line1", name);
    entry.put("Line2", date);
    entry.put("icon", cover);
    return entry;
}
With cover images in place
Figure 30. With cover images in place
Since the MultiList uses the GenericListCellRenderer internally we can use URLImage to dynamically fetch the data. This is discussed in the graphics section of this guide.
Going Further With the ListModel

Lets assume that GRRM was really prolific and wrote 1 million books. The default list model won’t make much sense in that case but we would still be able to render everything in a list model.

We’ll fake it a bit but notice that 1M components won’t be created even if we somehow scroll all the way down…​

The ListModel interface can be implemented by anyone in this case we just did a really stupid simple implementation:

class GRMMModel implements ListModel<Map<String,Object>> {
    @Override
    public Map<String, Object> getItemAt(int index) {
        int idx = index % 7;
        switch(idx) {
            case 0:
                return createListEntry("A Game of Thrones " + index, "1996");
            case 1:
                return createListEntry("A Clash Of Kings " + index, "1998");
            case 2:
                return createListEntry("A Storm Of Swords " + index, "2000");
            case 3:
                return createListEntry("A Feast For Crows " + index, "2005");
            case 4:
                return createListEntry("A Dance With Dragons " + index, "2011");
            case 5:
                return createListEntry("The Winds of Winter " + index, "2016 (please, please, please)");
            default:
                return createListEntry("A Dream of Spring " + index, "Ugh");
        }
    }

    @Override
    public int getSize() {
        return 1000000;
    }

    @Override
    public int getSelectedIndex() {
        return 0;
    }

    @Override
    public void setSelectedIndex(int index) {
    }

    @Override
    public void addDataChangedListener(DataChangedListener l) {
    }

    @Override
    public void removeDataChangedListener(DataChangedListener l) {
    }

    @Override
    public void addSelectionListener(SelectionListener l) {
    }

    @Override
    public void removeSelectionListener(SelectionListener l) {
    }

    @Override
    public void addItem(Map<String, Object> item) {
    }

    @Override
    public void removeItem(int index) {
    }
}

We can now replace the existing model by removing all the model related logic and changing the constructor call as such:

MultiList ml = new MultiList(new GRMMModel());
It took ages to scroll this far... This goes to a million...
Figure 31. It took ages to scroll this far…​ This goes to a million…​

List Cell Renderer

The Renderer is a simple interface with 2 methods:

public interface ListCellRenderer {
   //This method is called by the List for each item, when the List paints itself.
   public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected);

   //This method returns the List animated focus which is animated when list selection changes
   public Component getListFocusComponent(List list);
}

The most simple/naive implementation may choose to implement the renderer as follows:

public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){
     return new Label(value.toString());
}

public Component getListFocusComponent(List list){
     return null;
}

This will compile and work, but won’t give you much, notice that you won’t see the List selection move on the List, this is just because the renderer returns a Label with the same style regardless if it’s selected or not.

Now Let’s try to make it a bit more useful.

public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){
      Label l = new Label(value.toString());
if (isSelected) {
             l.setFocus(true);
             l.getAllStyles().setBgTransparency(100);
         } else {
             l.setFocus(false);
             l.getAllStyles().setBgTransparency(0);
        }
        return l;
}   public Component getListFocusComponent(List list){
      return null;
}

In this renderer we set the Label.setFocus(true) if it’s selected, calling to this method doesn’t really give the focus to the Label, it simply renders the label as selected.

Then we invoke Label.getAllStyles().setBgTransparency(100) to give the selection semi transparency, and 0 for full transparency if not selected.

That is still not very efficient because we create a new Label each time the method is invoked.

To make the code tighter, keep a reference to the Component or extend it as DefaultListCellRenderer does.

class MyRenderer extends Label implements ListCellRenderer {
    public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){
        setText(value.toString());
        if (isSelected) {
            setFocus(true);
            getAllStyles().setBgTransparency(100);
        } else {
            setFocus(false);
            getAllStyles().setBgTransparency(0);
        }
        return this;
        }
    }
}

Now Let’s have a look at a more advanced Renderer.

class ContactsRenderer extends Container implements ListCellRenderer {

 private Label name = new Label("");
 private Label email = new Label("");
 private Label pic = new Label("");

 private Label focus = new Label("");

 public ContactsRenderer() {
     setLayout(new BorderLayout());
     addComponent(BorderLayout.WEST, pic);
     Container cnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
     name.getAllStyles().setBgTransparency(0);
     name.getAllStyles().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
     email.getAllStyles().setBgTransparency(0);
     cnt.addComponent(name);
     cnt.addComponent(email);
     addComponent(BorderLayout.CENTER, cnt);

     focus.getStyle().setBgTransparency(100);
 }

 public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {

     Contact person = (Contact) value;
     name.setText(person.getName());
     email.setText(person.getEmail());
     pic.setIcon(person.getPic());
     return this;
 }

 public Component getListFocusComponent(List list) {
     return focus;
 }
}

In this renderer we want to render a Contact object to the Screen, we build the Component in the constructor and in the getListCellRendererComponent we simply update the Labels' texts according to the Contact object.

Notice that in this renderer we return a focus Label with semi transparency, as mentioned before, the focus component can be modified within this method.

For example, I can modify the focus Component to have an icon.

focus.getAllStyles().setBgTransparency(100);
try {
    focus.setIcon(Image.createImage("/duke.png"));
    focus.setAlignment(Component.RIGHT);
} catch (IOException ex) {
    ex.printStackTrace();
}

Generic List Cell Renderer

As part of the GUI builder work, we needed a way to customize rendering for a List, but the renderer/model approach seemed impossible to adapt to a GUI builder (it seems the Swing GUI builders had a similar issue). Our solution was to introduce the GenericListCellRenderer, which while introducing limitations and implementation requirements still manages to make life easier, both in the GUI builder and outside of it.

GenericListCellRenderer is a renderer designed to be as simple to use as a Component-Container hierarchy, we effectively crammed most of the common renderer use cases into one class. To enable that, we need to know the content of the objects within the model, so the GenericListCellRenderer assumes the model contains only Map objects. Since Maps can contain arbitrary data the list model is still quite generic and allows storing application specific data. Furthermore a Map can still be derived and extended to provide domain specific business logic.

The GenericListCellRenderer accepts two container instances (more later on why at least two, and not one), which it maps to individual Map entries within the model, by finding the appropriate components within the given container hierarchy. Components are mapped to the Map entries based on the name property of the component (getName/setName) and the key/value within the Map, e.g.:

For a model that contains a Map entry like this:

"Foo": "Bar"
"X": "Y"
"Not": "Applicable"
"Number": Integer(1)

A renderer will loop over the component hierarchy in the container, searching for components whose name matches Foo, X, Not, and Number, and assigning the appropriate value to them.

You can also use image objects as values, and they will be assigned to labels as expected. However, you can’t assign both an image and a text to a single label, since the key will be taken. That isn’t a big problem, since two labels can be used quite easily in such a renderer.

To make matters even more attractive the renderer seamlessly supports list tickering when appropriate, and if a CheckBox appears within the renderer, it will toggle a boolean flag within the Map seamlessly.

One issue that crops up with this approach is that, if a value is missing from the Map, it is treated as empty and the component is reset.

This can pose an issue if we hardcode an image or text within the renderer and we don’t want them replaced (e.g. an arrow graphic on a Label within the renderer). The solution for this is to name the component with Fixed in the end of the name, e.g. HardcodedIconFixed.

Naming a component within the renderer with $number will automatically set it as a counter component for the offset of the component within the list.

Styling the GenericListCellRenderer is slightly different, the renderer uses the UIID of the Container passed to the generic list cell renderer, and the background focus uses that same UIID with the word "Focus" appended to it.

It is important to notice that the generic list cell renderer will grant focus to the child components of the selected entry if they are focusable, thus changing the style of said entries. E.g. a Container might have a child Label that has one style when the parent container is unselected and another when it’s selected (focused), this can be easily achieved by defining the label as focusable. Notice that the component will never receive direct focus, since it is still part of a renderer.

Last but not least, the generic list cell renderer accepts two or four instances of a Container, rather than the obvious choice of accepting only one instance. This allows the renderer to treat the selected entry differently, which is especially important to tickering, although it’s also useful for the fisheye effect [3]. Since it might not be practical to seamlessly clone the Container for the renderer’s needs, Codename One expects the developer to provide two separate instances, they can be identical in all respects, but they must be separate instances for tickering to work. The renderer also allows for a fisheye effect, where the selected entry is actually different from the unselected entry in its structure, it also allows for a pinstripe effect, where odd/even rows have different styles (this is accomplished by providing 4 instances of the containers selected/unselected for odd/even).

The best way to learn about the generic list cell renderer and the Map model is by playing with them in the old GUI builder. Notice they can be used in code without any dependency on the GUI builder and can be quite useful at that.

Here is a simple example of a list with checkboxes that gets updated automatically:

com.codename1.ui.List list = new com.codename1.ui.List(createGenericListCellRendererModelData());
list.setRenderer(new GenericListCellRenderer(createGenericRendererContainer(), createGenericRendererContainer()));


private Container createGenericRendererContainer() {
    Label name = new Label();
    name.setFocusable(true);
    name.setName("Name");
    Label surname = new Label();
    surname.setFocusable(true);
    surname.setName("Surname");
    CheckBox selected = new CheckBox();
    selected.setName("Selected");
    selected.setFocusable(true);
    Container c = BorderLayout.center(name).
            add(BorderLayout.SOUTH, surname).
            add(BorderLayout.WEST, selected);
    c.setUIID("ListRenderer");
    return c;
}

private Object[] createGenericListCellRendererModelData() {
    Map<String,Object>[] data = new HashMap[5];
    data[0] = new HashMap<>();
    data[0].put("Name", "Shai");
    data[0].put("Surname", "Almog");
    data[0].put("Selected", Boolean.TRUE);
    data[1] = new HashMap<>();
    data[1].put("Name", "Chen");
    data[1].put("Surname", "Fishbein");
    data[1].put("Selected", Boolean.TRUE);
    data[2] = new HashMap<>();
    data[2].put("Name", "Ofir");
    data[2].put("Surname", "Leitner");
    data[3] = new HashMap<>();
    data[3].put("Name", "Yaniv");
    data[3].put("Surname", "Vakarat");
    data[4] = new HashMap<>();
    data[4].put("Name", "Meirav");
    data[4].put("Surname", "Nachmanovitch");
    return data;
}
GenericListCellRenderer demo code
Figure 32. GenericListCellRenderer demo code
Custom UIID Of Entry in GenenricListCellRenderer/MultiList

With MultiList/GenenricListCellRenderer one of the common issues is making a UI where a specific component within the list renderer has a different UIID style based on data. E.g. this can be helpful to mark a label within the list as red, for instance, in the case of a list of monetary transactions.

This can be achieved with a custom renderer, but that is a pretty difficult task.
GenericListCellRenderer (MultiList uses GenericListCellRenderer internally) has another option.

Normally, to build the model for a renderer of this type, we use something like:

map.put("componentName", "Component Value");

What if we want componentName to be red? Just use:

map.put("componentName_uiid", "red");

This will apply the UIID "red" to the component, which you can then style in the theme. Notice that once you start doing this, you need to define this entry for all entries, e.g.:

map.put("componentName_uiid", "blue");

Otherwise the component will stay red for the next entry (since the renderer acts like a rubber stamp).

Rendering Prototype

Because of the rendering architecture of a List its pretty hard to calculate the right preferred size for such a component. The default behavior includes querying a few entries from the model then constructing their renderers to get a "sample" of the preferred size value.

As you might guess this triggers a performance penalty that is paid with every reflow of the UI. The solution is to use setRenderingPrototype.

setRenderingPrototype accepts a "fake" value that represents a reasonably large amount of data and it will be used to calculate the preferred size. E.g. for a multiList that should render 2 lines of text with 20 characters and a 5mm square icon I can do something like this:

Map<String, Object> proto = new HashMap<>();
map.put("Line1", "WWWWWWWWWWWWWWWWWWWW");
map.put("Line2", "WWWWWWWWWWWWWWWWWWWW");
int mm5 = Display.getInstance().convertToPixels(5, true);
map.put("icon", Image.create(mm5, mm5));
myMultiList.setRenderingPrototype(map);

ComboBox

The ComboBox is a specialization of List that displays a single selected entry. When clicking that entry a popup is presented allowing the user to pick an entry from the full list of entries.

The ComboBox UI paradigm isn’t as common on OS’s such as iOS where there is no native equivalent to it. We recommend using either the Picker class or the AutoCompleteTextField.

ComboBox is notoriously hard to style properly as it relies on a complex dynamic of popup renderer and instantly visible renderer. The UIID for the ComboBox is ComboBox however if you set it to something else all the other UIID’s will also change their prefix. E.g. the ComboBoxPopup UIID will become MyNewUIIDPopup.

The combo box defines the following UIID’s by default:

  • ComboBox

  • ComboBoxItem

  • ComboBoxFocus

  • PopupContentPane

  • PopupItem

  • PopupFocus

The ComboBox also defines theme constants that allow some native themes to manipulate its behavior e.g.:

  • popupTitleBool - shows the "label for" value as the title of the popup dialog

  • popupCancelBodyBool - Adds a cancel button into the popup dialog

  • centeredPopupBool - shows the popup dialog in the center of the screen instead of under the popup

  • otherPopupRendererBool - Uses a different list cell render for the popup than the one used for the ComboBox itself. When this is false PopupItem & PopupFocus become irrelevant. Notice that the Android native theme defines this to true.

Since a ComboBox is really a List you can use everything we learned about a List to build a ComboBox including models, GenericListCellRenderer etc.

E.g. the demo below uses the GRRM demo data from above to build a ComboBox:

Form hi = new Form("ComboBox", new BoxLayout(BoxLayout.Y_AXIS));
ComboBox<Map<String, Object>> combo = new ComboBox<> (
        createListEntry("A Game of Thrones", "1996"),
        createListEntry("A Clash Of Kings", "1998"),
        createListEntry("A Storm Of Swords", "2000"),
        createListEntry("A Feast For Crows", "2005"),
        createListEntry("A Dance With Dragons", "2011"),
        createListEntry("The Winds of Winter", "2016 (please, please, please)"),
        createListEntry("A Dream of Spring", "Ugh"));

combo.setRenderer(new GenericListCellRenderer<>(new MultiButton(), new MultiButton()));
GRRM ComboBox
Figure 33. GRRM ComboBox

Slider

A Slider is an empty component that can be filled horizontally or vertically to allow indicating progress, setting volume etc. It can be editable to allow the user to determine its value or none editable to just relay that information to the user. It can have a thumb on top to show its current position.

Slider
Figure 34. Slider

The interesting part about the slider is that it has two separate style UIID’s, Slider & SliderFull. The Slider UIID is always painted and SliderFull is rendered on top based on the amount the Slider should be filled.

Slider is highly customizable e.g. a slider can be used to replicate a 5 star rating widget as such. Notice that this slider will only work when its given its preferred size otherwise additional stars will appear. That’s why we place it within a FlowLayout:

Form hi = new Form("Star Slider", new BoxLayout(BoxLayout.Y_AXIS));
hi.add(FlowLayout.encloseCenter(createStarRankSlider()));
hi.show();

The slider itself is initialized in the code below. Notice that you can achieve almost the same result using a theme by setting the Slider & SliderFull UIID’s (both in selected & unselected states).

In fact doing this in the theme might be superior as you could use one image that contains 5 stars already and that way you won’t need the preferred size hack below:

private void initStarRankStyle(Style s, Image star) {
    s.setBackgroundType(Style.BACKGROUND_IMAGE_TILE_BOTH);
    s.setBorder(Border.createEmpty());
    s.setBgImage(star);
    s.setBgTransparency(0);
}

private Slider createStarRankSlider() {
    Slider starRank = new Slider();
    starRank.setEditable(true);
    starRank.setMinValue(0);
    starRank.setMaxValue(10);
    Font fnt = Font.createTrueTypeFont("native:MainLight", "native:MainLight").
            derive(Display.getInstance().convertToPixels(5, true), Font.STYLE_PLAIN);
    Style s = new Style(0xffff33, 0, fnt, (byte)0);
    Image fullStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage();
    s.setOpacity(100);
    s.setFgColor(0);
    Image emptyStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage();
    initStarRankStyle(starRank.getSliderEmptySelectedStyle(), emptyStar);
    initStarRankStyle(starRank.getSliderEmptyUnselectedStyle(), emptyStar);
    initStarRankStyle(starRank.getSliderFullSelectedStyle(), fullStar);
    initStarRankStyle(starRank.getSliderFullUnselectedStyle(), fullStar);
    starRank.setPreferredSize(new Dimension(fullStar.getWidth() * 5, fullStar.getHeight()));
    return starRank;
}
private void showStarPickingForm() {
  Form hi = new Form("Star Slider", new BoxLayout(BoxLayout.Y_AXIS));
  hi.add(FlowLayout.encloseCenter(createStarRankSlider()));
  hi.show();
}
Star Slider set to 5 (its between 0 - 10)
Figure 35. Star Slider set to 5 (its between 0 - 10)
This slider goes all the way to 0 stars which is less common. You can use a Label to represent the first star and have the slider work between 0 - 8 values to provide 4 additional stars.

Table

Table is a composite component (but it isn’t a lead component), this means it is a subclass of Container. It’s effectively built from multiple components.

Table is heavily based on the TableLayout class. It’s important to be familiar with that layout manager when working with Table.

Here is a trivial sample of using the standard table component:

Form hi = new Form("Table", new BorderLayout());
TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] {
    {"Row 1", "Row A", "Row X"},
    {"Row 2", "Row B", "Row Y"},
    {"Row 3", "Row C", "Row Z"},
    {"Row 4", "Row D", "Row K"},
    }) {
        public boolean isCellEditable(int row, int col) {
            return col != 0;
        }
    };
Table table = new Table(model);
hi.add(BorderLayout.CENTER, table);
hi.show();
Simple Table usage
Figure 36. Simple Table usage
In the sample above the title area and first column aren’t editable. The other two columns are editable.

The more "interesting" capabilities of the Table class can be utilized via the TableLayout. You can use the layout constraints (also exposed in the table class) to create spanning and elaborate UI’s.

E.g.:

Form hi = new Form("Table", new BorderLayout());
TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] {
    {"Row 1", "Row A", "Row X"},
    {"Row 2", "Row B can now stretch", null},
    {"Row 3", "Row C", "Row Z"},
    {"Row 4", "Row D", "Row K"},
    }) {
        public boolean isCellEditable(int row, int col) {
            return col != 0;
        }
    };
Table table = new Table(model) {
    @Override
    protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) {
        TableLayout.Constraint con =  super.createCellConstraint(value, row, column);
        if(row == 1 && column == 1) {
            con.setHorizontalSpan(2);
        }
        con.setWidthPercentage(33);
        return con;
    }
};
hi.add(BorderLayout.CENTER, table);
Table with spanning & fixed widths to 33%
Figure 37. Table with spanning & fixed widths to 33%

In order to customize the table cell behavior you can derive the Table to create a "renderer like" widget, however unlike the list this component is "kept" and used as is. This means you can bind listeners to this component and work with it as you would with any other component in Codename One.

So lets fix the example above to include far more capabilities:

Table table = new Table(model) {
    @Override
    protected Component createCell(Object value, int row, int column, boolean editable) { (1)
        Component cell;
        if(row == 1 && column == 1) {  (2)
            Picker p = new Picker();
            p.setType(Display.PICKER_TYPE_STRINGS);
            p.setStrings("Row B can now stretch", "This is a good value", "So Is This", "Better than text field");
            p.setSelectedString((String)value);  (3)
            p.setUIID("TableCell");
            p.addActionListener((e) -> getModel().setValueAt(row, column, p.getSelectedString()));  (4)
            cell = p;
        } else {
            cell = super.createCell(value, row, column, editable);
        }
        if(row > -1 && row % 2 == 0) {  (5)
            // pinstripe effect
            cell.getAllStyles().setBgColor(0xeeeeee);
            cell.getAllStyles().setBgTransparency(255);
        }
        return cell;
    }

    @Override
    protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) {
        TableLayout.Constraint con =  super.createCellConstraint(value, row, column);
        if(row == 1 && column == 1) {
            con.setHorizontalSpan(2);
        }
        con.setWidthPercentage(33);
        return con;
    }
};
1 The createCell method is invoked once per component but is similar conceptually to the List renderer. Notice that it doesn’t return a "rubber stamp" though, it returns a full component.
2 We only apply the picker to one cell for simplicities sake.
3 We need to set the value of the component manually, this is crucial since the Table doesn’t "see" this.
4 We need to track the event and update the model in this case as the Table isn’t aware of the data change.
5 We set the "pinstripe" effect by coloring even rows. Notice that unlike renderers we only need to apply the coloring once as the Components are stateful.
Table with customize cells using the pinstripe effect
Figure 38. Table with customize cells using the pinstripe effect
Picker table cell during edit
Figure 39. Picker table cell during edit

To line wrap table cells we can just override the createCell method and return a TextArea instead of a TextField since the TextArea defaults to the multi-line behavior this should work seamlessly. E.g.:

Form hi = new Form("Table", new BorderLayout());
TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] {
    {"Row 1", "Row A", "Row X"},
    {"Row 2", "Row B can now stretch very long line that should span multiple rows as much as possible", "Row Y"},
    {"Row 3", "Row C", "Row Z"},
    {"Row 4", "Row D", "Row K"},
    }) {
        public boolean isCellEditable(int row, int col) {
            return col != 0;
        }
    };
Table table = new Table(model) {
    @Override
    protected Component createCell(Object value, int row, int column, boolean editable) {
        TextArea ta = new TextArea((String)value);
        ta.setUIID("TableCell");
        return ta;
    }

    @Override
    protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) {
        TableLayout.Constraint con =  super.createCellConstraint(value, row, column);
        con.setWidthPercentage(33);
        return con;
    }
};
hi.add(BorderLayout.CENTER, table);
hi.show();
Notice that we don’t really need to do anything else as binding to the TextArea is builtin to the Table.
We must set the column width constraint when we want multi-line to work. Otherwise the preferred size of the column might be too wide and the remaining columns might not have space left.
Multiline table cell in portrait mode
Figure 40. Multiline table cell in portrait mode
Multiline table cell in landscape mode. Notice the cell row count adapts seamlessly
Figure 41. Multiline table cell in landscape mode. Notice the cell row count adapts seamlessly

Tree

Tree allows displaying hierarchical data such as folders and files in a collapsible/expandable UI. Like the Table it is a composite component (but it isn’t a lead component). Like the Table it works in consort with a model to construct its user interface on the fly but doesn’t use a stateless renderer (as List does).

The data of the Tree arrives from a model model e.g. this:

class StringArrayTreeModel implements TreeModel {
    String[][] arr = new String[][] {
            {"Colors", "Letters", "Numbers"},
            {"Red", "Green", "Blue"},
            {"A", "B", "C"},
            {"1", "2", "3"}
        };

    public Vector getChildren(Object parent) {
        if(parent == null) {
            Vector v = new Vector();
            for(int iter = 0 ; iter < arr[0].length ; iter++) {
                v.addElement(arr[0][iter]);
            }
            return v;
        }
        Vector v = new Vector();
        for(int iter = 0 ; iter < arr[0].length ; iter++) {
            if(parent == arr[0][iter]) {
                if(arr.length > iter + 1 && arr[iter + 1] != null) {
                    for(int i = 0 ; i < arr[iter + 1].length ; i++) {
                        v.addElement(arr[iter + 1][i]);
                    }
                }
            }
        }
        return v;
    }

    public boolean isLeaf(Object node) {
        Vector v = getChildren(node);
        return v == null || v.size() == 0;
    }
}

Tree dt = new Tree(new StringArrayTreeModel());

Will result in this:

Tree
Figure 42. Tree
Since Tree is hierarchy based we can’t have a simple model like we have for the Table as deep hierarchy is harder to represent with arrays.

A more practical "real world" example would be working with XML data. We can use something like this to show an XML Tree:

Form hi = new Form("XML Tree", new BorderLayout());
InputStream is = Display.getInstance().getResourceAsStream(getClass(), "/build.xml");
try(Reader r = new InputStreamReader(is, "UTF-8")) {
    Element e = new XMLParser().parse(r);
    Tree xmlTree = new Tree(new XMLTreeModel(e)) {
        @Override
        protected String childToDisplayLabel(Object child) {
            if(child instanceof Element) {
                return ((Element)child).getTagName();
            }
            return child.toString();
        }
    };
    hi.add(BorderLayout.CENTER, xmlTree);
} catch(IOException err) {
    Log.e(err);
}
The try(Stream) syntax is a try with resources clogic that implicitly closes the stream.
XML Tree
Figure 43. XML Tree

The model for the XML hierarchy is implemented as such:

class XMLTreeModel implements TreeModel {
    private Element root;
    public XMLTreeModel(Element e) {
        root = e;
    }

    public Vector getChildren(Object parent) {
        if(parent == null) {
            Vector c = new Vector();
            c.addElement(root);
            return c;
        }
        Vector result = new Vector();
        Element e = (Element)parent;
        for(int iter = 0 ; iter < e.getNumChildren() ; iter++) {
            result.addElement(e.getChildAt(iter));
        }
        return result;
    }

    public boolean isLeaf(Object node) {
        Element e = (Element)node;
        return e.getNumChildren() == 0;
    }
}

ShareButton

ShareButton is a button you can add into the UI to let a user share an image or block of text.

The ShareButton uses a set of predefined share options on the simulator. On Android & iOS the ShareButton is mapped to the OS native sharing functionality and can share the image/text with the services configured on the device (e.g. Twitter, Facebook etc.).

Sharing text is trivial but to share an image we need to save it to the FileSystemStorage. Notice that saving to Storage won’t work!

In the sample code below we take a screenshot which is saved to FileSystemStorage for sharing:

Form hi = new Form("ShareButton");
ShareButton sb = new ShareButton();
sb.setText("Share Screenshot");
hi.add(sb);

Image screenshot = Image.createImage(hi.getWidth(), hi.getHeight());
hi.revalidate();
hi.setVisible(true);
hi.paintComponent(screenshot.getGraphics(), true);

String imageFile = FileSystemStorage.getInstance().getAppHomePath() + "screenshot.png";
try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(imageFile)) {
    ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1);
} catch(IOException err) {
    Log.e(err);
}
sb.setImageToShare(imageFile, "image/png");
The share button running on the simulator
Figure 44. The share button running on the simulator
ShareButton behaves very differently on the device…​
The share button running on the Android device and screenshot sent into twitter
Figure 45. The share button running on the Android device and screenshot sent into twitter
The ShareButton features some share service classes to allow plugging in additional share services. However, this functionality is only relevant to devices where native sharing isn’t supported. So this code isn’t used on iOS/Android…​

Tabs

The Tabs Container arranges components into groups within "tabbed" containers. Tabs is a container type that allows leafing through its children using labeled toggle buttons. The tabs can be placed in multiple different ways (top, bottom, left or right) with the default being determined by the platform. This class also allows swiping between components to leaf between said tabs (for this purpose the tabs themselves can also be hidden).

Since Tabs are a Container its a common mistake to try and add a Tab using the add method. That method won’t work since a Tab can have both an Image and text String associated with it.

Form hi = new Form("Tabs", new BorderLayout());

Tabs t = new Tabs();
Style s = UIManager.getInstance().getComponentStyle("Tab");
FontImage icon1 = FontImage.createMaterial(FontImage.MATERIAL_QUESTION_ANSWER, s);

Container container1 = BoxLayout.encloseY(new Label("Label1"), new Label("Label2"));
t.addTab("Tab1", icon1, container1);
t.addTab("Tab2", new SpanLabel("Some text directly in the tab"));

hi.add(BorderLayout.CENTER, t);
Simple usage of Tabs
Figure 46. Simple usage of Tabs

A common usage for Tabs is the the swipe to proceed effect which is very common in iOS applications. In the code below we use RadioButton and LayeredLayout with hidden tabs to produce that effect:

Form hi = new Form("Swipe Tabs", new LayeredLayout());
Tabs t = new Tabs();
t.hideTabs();

Style s = UIManager.getInstance().getComponentStyle("Button");
FontImage radioEmptyImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, s);
FontImage radioFullImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, s);
((DefaultLookAndFeel)UIManager.getInstance().getLookAndFeel()).setRadioButtonImages(radioFullImage, radioEmptyImage, radioFullImage, radioEmptyImage);

Container container1 = BoxLayout.encloseY(new Label("Swipe the tab to see more"),
        new Label("You can put anything here"));
t.addTab("Tab1", container1);
t.addTab("Tab2", new SpanLabel("Some text directly in the tab"));

RadioButton firstTab = new RadioButton("");
RadioButton secondTab = new RadioButton("");
firstTab.setUIID("Container");
secondTab.setUIID("Container");
new ButtonGroup(firstTab, secondTab);
firstTab.setSelected(true);
Container tabsFlow = FlowLayout.encloseCenter(firstTab, secondTab);

hi.add(t);
hi.add(BorderLayout.south(tabsFlow));

t.addSelectionListener((i1, i2) -> {
    switch(i2) {
        case 0:
            if(!firstTab.isSelected()) {
                firstTab.setSelected(true);
            }
            break;
        case 1:
            if(!secondTab.isSelected()) {
                secondTab.setSelected(true);
            }
            break;
     }
});
Swipeable Tabs with an iOS carousel effect page 1
Figure 47. Swipeable Tabs with an iOS carousel effect page 1
Swipeable Tabs with an iOS carousel effect page 2
Figure 48. Swipeable Tabs with an iOS carousel effect page 2
Notice that we used setRadioButtonImages to explicitly set the radio button images to the look we want for the carousel.

MediaManager & MediaPlayer

MediaPlayer is a peer component, understanding this is crucial if your application depends on such a component. You can learn about peer components and their issues here.

The MediaPlayer allows you to control video playback. To use the MediaPlayer we need to first load the Media object from the MediaManager.

The MediaManager is the core class responsible for media interaction in Codename One.

You should also check out the Capture class for things that aren’t covered by the MediaManager.

In the demo code below we use the gallery functionality to pick a video from the device’s video gallery.

final Form hi = new Form("MediaPlayer", new BorderLayout());
hi.setToolbar(new Toolbar());
Style s = UIManager.getInstance().getComponentStyle("Title");
FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_VIDEO_LIBRARY, s);
hi.getToolbar().addCommandToRightBar("", icon, (evt) -> {
    Display.getInstance().openGallery((e) -> {
        if(e != null && e.getSource() != null) {
            String file = (String)e.getSource();
            try {
                Media video = MediaManager.createMedia(file, true);
                hi.removeAll();
                hi.add(BorderLayout.CENTER, new MediaPlayer(video));
                hi.revalidate();
            } catch(IOException err) {
                Log.e(err);
            }
        }
    }, Display.GALLERY_VIDEO);
});
hi.show();
Video playback running on the simulator
Figure 49. Video playback running on the simulator
Video playback running on an Android device. Notice the native playback controls that appear when the video is tapped
Figure 50. Video playback running on an Android device. Notice the native playback controls that appear when the video is tapped
Video playback in the simulator will only work with JavaFX enabled. This is the default for Java 8 or newer so we recommend using that.

ImageViewer

The ImageViewer allows us to inspect, zoom and pan into an image. It also allows swiping between images if you have a set of images (using an image list model).

The ImageViewer is a complex rich component designed for user interaction. If you just want to display an image use Label if you want the image to scale seamlessly use ScaleImageLabel.

You can use the ImageViewer as a tool to view a single image which allows you to zoom in/out to that image as such:

Form hi = new Form("ImageViewer", new BorderLayout());
ImageViewer iv = new ImageViewer(duke);
hi.add(BorderLayout.CENTER, iv);
You can simulate pinch to zoom on the simulator by dragging the right button away from the top left corner to zoom in and towards the top left corner to zoom out. On Mac touchpads you can drag two fingers to achieve that.
ImageViewer as the demo loads with the image from the default icon
Figure 51. ImageViewer as the demo loads with the image from the default icon
ImageViewer zoomed in
Figure 52. ImageViewer zoomed in

We can work with a list of images to produce a swiping effect for the image viewer where you can swipe from one image to the next and also zoom in/out on a specific image:

Form hi = new Form("ImageViewer", new BorderLayout());

Image red = Image.createImage(100, 100, 0xffff0000);
Image green = Image.createImage(100, 100, 0xff00ff00);
Image blue = Image.createImage(100, 100, 0xff0000ff);
Image gray = Image.createImage(100, 100, 0xffcccccc);

ImageViewer iv = new ImageViewer(red);
iv.setImageList(new DefaultListModel<>(red, green, blue, gray));
hi.add(BorderLayout.CENTER, iv);
An ImageViewer with multiple elements is indistinguishable from a single ImageViewer with the exception of swipe
Figure 53. An ImageViewer with multiple elements is indistinguishable from a single ImageViewer with the exception of swipe

Notice that we use a ListModel to allow swiping between images.

EncodedImage’s aren’t always fully loaded and so when you swipe if the images are really large you might see delays!

You can dynamically download images directly into the ImageViewer with a custom list model like this:

Form hi = new Form("ImageViewer", new BorderLayout());
final EncodedImage placeholder = EncodedImage.createFromImage(
        FontImage.createMaterial(FontImage.MATERIAL_SYNC, s).
                scaled(300, 300), false);

class ImageList implements ListModel<Image> {
    private int selection;
    private String[] imageURLs = {
        "http://awoiaf.westeros.org/images/thumb/9/93/AGameOfThrones.jpg/300px-AGameOfThrones.jpg",
        "http://awoiaf.westeros.org/images/thumb/3/39/AClashOfKings.jpg/300px-AClashOfKings.jpg",
        "http://awoiaf.westeros.org/images/thumb/2/24/AStormOfSwords.jpg/300px-AStormOfSwords.jpg",
        "http://awoiaf.westeros.org/images/thumb/a/a3/AFeastForCrows.jpg/300px-AFeastForCrows.jpg",
        "http://awoiaf.westeros.org/images/7/79/ADanceWithDragons.jpg"
    };
    private Image[] images;
    private EventDispatcher listeners = new EventDispatcher();

    public ImageList() {
        this.images = new EncodedImage[imageURLs.length];
    }

    public Image getItemAt(final int index) {
        if(images[index] == null) {
            images[index] = placeholder;
            Util.downloadUrlToStorageInBackground(imageURLs[index], "list" + index, (e) -> {
                    try {
                        images[index] = EncodedImage.create(Storage.getInstance().createInputStream("list" + index));
                        listeners.fireDataChangeEvent(index, DataChangedListener.CHANGED);
                    } catch(IOException err) {
                        err.printStackTrace();
                    }
            });
        }
        return images[index];
    }

    public int getSize() {
        return imageURLs.length;
    }

    public int getSelectedIndex() {
        return selection;
    }

    public void setSelectedIndex(int index) {
        selection = index;
    }

    public void addDataChangedListener(DataChangedListener l) {
        listeners.addListener(l);
    }

    public void removeDataChangedListener(DataChangedListener l) {
        listeners.removeListener(l);
    }

    public void addSelectionListener(SelectionListener l) {
    }

    public void removeSelectionListener(SelectionListener l) {
    }

    public void addItem(Image item) {
    }

    public void removeItem(int index) {
    }
};

ImageList imodel = new ImageList();

ImageViewer iv = new ImageViewer(imodel.getItemAt(0));
iv.setImageList(imodel);
hi.add(BorderLayout.CENTER, iv);
Dynamically fetching an image URL from the internet
Figure 54. Dynamically fetching an image URL from the internet [4]

This fetches the images in the URL asynchronously and fires a data change event when the data arrives to automatically refresh the ImageViewer when that happens.

ScaleImageLabel & ScaleImageButton

ScaleImageLabel & ScaleImageButton allow us to position an image that will grow/shrink to fit available space. In that sense they differ from Label & Button which keeps the image at the same size.

The default UIID of ScaleImageLabel is “Label”, however the default UIID of ScaleImageButton is “ScaleImageButton”. The reasoning for the difference is that the “Button” UIID includes a border and a lot of legacy.

You can use ScaleImageLabel/ScaleImageButton interchangeably. The only major difference between these components is the buttons ability to handle click events/focus.

Here is a simple example that also shows the difference between the scale to fill and scale to fit modes:

TableLayout tl = new TableLayout(2, 2);
Form hi = new Form("ScaleImageButton/Label", tl);
Style s = UIManager.getInstance().getComponentStyle("Button");
Image icon = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s);
ScaleImageLabel fillLabel = new ScaleImageLabel(icon);
fillLabel.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
ScaleImageButton fillButton = new ScaleImageButton(icon);
fillButton.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
hi.add(tl.createConstraint().widthPercentage(20), new ScaleImageButton(icon)).
        add(tl.createConstraint().widthPercentage(80), new ScaleImageLabel(icon)).
        add(fillLabel).
        add(fillButton);
hi.show();
ScaleImageLabel/Button
Figure 55. ScaleImageLabel/Button, the top row includes scale to fit versions (the default) whereas the bottom row includes the scale to fill versions
When styling these components keep in mind that changing attributes such as background behavior might cause an issue with their functionality or might not work.

Toolbar

The Toolbar API provides deep customization of the title bar area with more flexibility e.g. placing a TextField for search or buttons in arbitrary title area positions. The Toolbar API replicates some of the native functionality available on Android/iOS and integrates with features such as the side menu to provide very fine grained control over the title area behavior.

The Toolbar needs to be installed into the Form in order for it to work. You can setup the Toolbar in one of these three ways:

  1. form.setToolbar(new Toolbar()); - allows you to activate the Toolbar to a specific Form and not for the entire application

  2. Toolbar.setGlobalToolbar(true); - enables the Toolbar for all the forms in the app

  3. Theme constant globalToobarBool - this is equivalent to Toolbar.setGlobalToolbar(true);

The basic functionality of the Toolbar includes the ability to add a command to the following 4 places:

  • Left side of the title - addCommandToLeftBar

  • Right side of the title - addCommandToRightBar

  • Side menu bar (the drawer that opens when you click the icon on the top left or swipe the screen from left to right) - addCommandToSideMenu

  • Overflow menu (the menu that opens when you tap the 3 vertical dots in the top right corner) - addCommandToOverflowMenu

The code below provides a brief overview of these options:

Toolbar.setGlobalToolbar(true);

Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS));
hi.getToolbar().addCommandToLeftBar("Left", icon, (e) -> Log.p("Clicked"));
hi.getToolbar().addCommandToRightBar("Right", icon, (e) -> Log.p("Clicked"));
hi.getToolbar().addCommandToOverflowMenu("Overflow", icon, (e) -> Log.p("Clicked"));
hi.getToolbar().addCommandToSideMenu("Sidemenu", icon, (e) -> Log.p("Clicked"));
hi.show();
The Toolbar
Figure 56. The Toolbar
The default sidemenu of the Toolbar
Figure 57. The default sidemenu of the Toolbar
The overflow menu of the Toolbar
Figure 58. The overflow menu of the Toolbar

Normally you can just set a title with a String but if you would want the component to be a text field or a multi line label you can use setTitleComponent(Component) which allows you to install any component into the title area.

The code below demonstrates searching using custom code however the Toolbar also has builtin support for search covered in the next section

The customization of the title area allows for some pretty powerful UI effects e.g. the code below allows searching dynamically within a set of entries and uses some very neat tricks:

Toolbar.setGlobalToolbar(true);
Style s = UIManager.getInstance().getComponentStyle("Title");

Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS));
TextField searchField = new TextField("", "Toolbar Search"); (1)
searchField.getHintLabel().setUIID("Title");
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(Component.LEFT);
hi.getToolbar().setTitleComponent(searchField);
FontImage searchIcon = FontImage.createMaterial(FontImage.MATERIAL_SEARCH, s);
searchField.addDataChangeListener((i1, i2) -> { (2)
    String t = searchField.getText();
    if(t.length() < 1) {
        for(Component cmp : hi.getContentPane()) {
            cmp.setHidden(false);
            cmp.setVisible(true);
        }
    } else {
        t = t.toLowerCase();
        for(Component cmp : hi.getContentPane()) {
            String val = null;
            if(cmp instanceof Label) {
                val = ((Label)cmp).getText();
            } else {
                if(cmp instanceof TextArea) {
                    val = ((TextArea)cmp).getText();
                } else {
                    val = (String)cmp.getPropertyValue("text");
                }
            }
            boolean show = val != null && val.toLowerCase().indexOf(t) > -1;
            cmp.setHidden(!show); (3)
            cmp.setVisible(show);
        }
    }
    hi.getContentPane().animateLayout(250);
});
hi.getToolbar().addCommandToRightBar("", searchIcon, (e) -> {
    searchField.startEditingAsync(); (4)
});

hi.add("A Game of Thrones").
        add("A Clash Of Kings").
        add("A Storm Of Swords").
        add("A Feast For Crows").
        add("A Dance With Dragons").
        add("The Winds of Winter").
        add("A Dream of Spring");
hi.show();
1 We use a TextField the whole time and just style it to make it (and its hint) look like a regular title. An alternative way is to replace the title component dynamically.
2 We use the DataChangeListener to update the search results as we type them.
3 Hidden & Visible use the opposite flag values to say similar things (e.g. when hidden is set to false you would want to set visible to true).
Visible indicates whether a component can be seen. It will still occupy the physical space on the screen even when it isn’t visible. Hidden will remove the space occupied by the component from the screen, but some code might still try to paint it. Normally, visible is redundant but we use it with hidden for good measure.
4 The search button is totally unnecessary here. We can just click the TextField!
However, that isn’t intuitive to most users so we added the button to start editing.
Search field within the toolbar
Figure 59. Search field within the toolbar
Search field after typing a couple of letters
Figure 60. Search field after typing a couple of letters

Search Mode

While you can implement search manually using the builtin search offers a simpler and more uniform UI.

Builtin toolbar search functionality
Figure 61. Builtin toolbar search functionality

You can customize the appearance of the search bar by using the UIID’s: ToolbarSearch, TextFieldSearch & TextHintSearch.

In the sample below we fetch all the contacts from the device and enable search thru them, notice it expects and image called duke.png which is really just the default Codename One icon renamed and placed in the src folder:

Image duke = null;
try {
    duke = Image.createImage("/duke.png");
} catch(IOException err) {
    Log.e(err);
}
int fiveMM = Display.getInstance().convertToPixels(5);
final Image finalDuke = duke.scaledWidth(fiveMM);
Toolbar.setGlobalToolbar(true);
Form hi = new Form("Search", BoxLayout.y());
hi.add(new InfiniteProgress());
Display.getInstance().scheduleBackgroundTask(()-> {
    // this will take a while...
    Contact[] cnts = Display.getInstance().getAllContacts(true, true, true, true, false, false);
    Display.getInstance().callSerially(() -> {
        hi.removeAll();
        for(Contact c : cnts) {
            MultiButton m = new MultiButton();
            m.setTextLine1(c.getDisplayName());
            m.setTextLine2(c.getPrimaryPhoneNumber());
            Image pic = c.getPhoto();
            if(pic != null) {
                m.setIcon(fill(pic, finalDuke.getWidth(), finalDuke.getHeight()));
            } else {
                m.setIcon(finalDuke);
            }
            hi.add(m);
        }
        hi.revalidate();
    });
});

hi.getToolbar().addSearchCommand(e -> {
    String text = (String)e.getSource();
    if(text == null || text.length() == 0) {
        // clear search
        for(Component cmp : hi.getContentPane()) {
            cmp.setHidden(false);
            cmp.setVisible(true);
        }
        hi.getContentPane().animateLayout(150);
    } else {
        text = text.toLowerCase();
        for(Component cmp : hi.getContentPane()) {
            MultiButton mb = (MultiButton)cmp;
            String line1 = mb.getTextLine1();
            String line2 = mb.getTextLine2();
            boolean show = line1 != null && line1.toLowerCase().indexOf(text) > -1 ||
                    line2 != null && line2.toLowerCase().indexOf(text) > -1;
            mb.setHidden(!show);
            mb.setVisible(show);
        }
        hi.getContentPane().animateLayout(150);
    }
}, 4);

hi.show();

Title Animations

Modern UI’s often animate the title upon scrolling to balance the highly functional smaller title advantage with the gorgeous large image based title. This is pretty easy to do with the Toolbar API thru the Title animation API.

The code below shows off an attractive title based on a book by GRRM on top of text [5] that is scrollable. As the text is scrolled the title fades out.

Toolbar.setGlobalToolbar(true);

Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS));
EncodedImage placeholder = EncodedImage.createFromImage(Image.createImage(hi.getWidth(), hi.getWidth() / 5, 0xffff0000), true);
URLImage background = URLImage.createToStorage(placeholder, "400px-AGameOfThrones.jpg",
        "http://awoiaf.westeros.org/images/thumb/9/93/AGameOfThrones.jpg/400px-AGameOfThrones.jpg");
background.fetch();
Style stitle = hi.getToolbar().getTitleComponent().getUnselectedStyle();
stitle.setBgImage(background);
stitle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stitle.setPaddingUnit(Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS);
stitle.setPaddingTop(15);
SpanButton credit = new SpanButton("This excerpt is from A Wiki Of Ice And Fire. Please check it out by clicking here!");
credit.addActionListener((e) -> Display.getInstance().execute("http://awoiaf.westeros.org/index.php/A_Game_of_Thrones"));
hi.add(new SpanLabel("A Game of Thrones is the first of seven planned novels in A Song of Ice and Fire, an epic fantasy series by American author George R. R. Martin. It was first published on 6 August 1996. The novel was nominated for the 1998 Nebula Award and the 1997 World Fantasy Award,[1] and won the 1997 Locus Award.[2] The novella Blood of the Dragon, comprising the Daenerys Targaryen chapters from the novel, won the 1997 Hugo Award for Best Novella. ")).
        add(new Label("Plot introduction", "Heading")).
        add(new SpanLabel("A Game of Thrones is set in the Seven Kingdoms of Westeros, a land reminiscent of Medieval Europe. In Westeros the seasons last for years, sometimes decades, at a time.\n\n" +
            "Fifteen years prior to the novel, the Seven Kingdoms were torn apart by a civil war, known alternately as \"Robert's Rebellion\" and the \"War of the Usurper.\" Prince Rhaegar Targaryen kidnapped Lyanna Stark, arousing the ire of her family and of her betrothed, Lord Robert Baratheon (the war's titular rebel). The Mad King, Aerys II Targaryen, had Lyanna's father and eldest brother executed when they demanded her safe return. Her second brother, Eddard, joined his boyhood friend Robert Baratheon and Jon Arryn, with whom they had been fostered as children, in declaring war against the ruling Targaryen dynasty, securing the allegiances of House Tully and House Arryn through a network of dynastic marriages (Lord Eddard to Catelyn Tully and Lord Arryn to Lysa Tully). The powerful House Tyrell continued to support the King, but House Lannister and House Martell both stalled due to insults against their houses by the Targaryens. The civil war climaxed with the Battle of the Trident, when Prince Rhaegar was killed in battle by Robert Baratheon. The Lannisters finally agreed to support King Aerys, but then brutally... ")).
        add(credit);

ComponentAnimation title = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200);
hi.getAnimationManager().onTitleScrollAnimation(title);
hi.show();
The Toolbar starts with the large URLImage fetched from the web
Figure 62. The Toolbar starts with the large URLImage fetched from the web
As we scroll down the image fades and the title shrinks in size returning to the default UIID look
Figure 63. As we scroll down the image fades and the title shrinks in size returning to the default UIID look
As scrolling continues the title reaches standard size
Figure 64. As scrolling continues the title reaches standard size

Almost all of the code above just creates the "look" of the application. The key piece of code above is this:

ComponentAnimation title = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200);
hi.getAnimationManager().onTitleScrollAnimation(title);

In the first line we create a style animation that will translate the style from the current settings to the destination UIID (the first argument) within 200 pixels of scrolling. We then bind this animation to the title scrolling animation event.

BrowserComponent & WebBrowser

BrowserComponent is a peer component, understanding this is crucial if your application depends on such a component. You can learn about peer components and their issues here.

The WebBrowser component shows the native device web browser when supported by the device and the HTMLComponent when the web browser isn’t supported on the given device. If you only intend to target smartphones you should use the BrowserComponent directly instead of the WebBrowser.

The BrowserComponent can point at an arbitrary URL to load it:

Form hi = new Form("Browser", new BorderLayout());
BrowserComponent browser = new BrowserComponent();
browser.setURL("https://www.codenameone.com/");
hi.add(BorderLayout.CENTER, browser);
Browser Component showing the Codename One website on the simulator
Figure 65. Browser Component showing the Codename One website on the simulator
The scrollbars only appear in the simulator, device versions of the browser component act differently and support touch scrolling.
A BrowserComponent should be in the center of a BorderLayout. Otherwise its preferred size might be zero before the HTML finishes loading/layout in the native layer and layout might be incorrect as a result.

You can use WebBrowser and BrowserComponent interchangeably for most basic usage. However, if you need access to JavaScript or native browser functionality then there is really no use in going thru the WebBrowser abstraction.

The BrowserComponent has full support for executing local web pages from within the jar. The basic support uses the jar:/// URL as such:

BrowserComponent wb = new BrowserComponent();
wb.setURL("jar:///Page.html");
On Android a native indicator might show up when the web page is loading. This can be disabled using the Display.getInstance().setProperty("WebLoadingHidden", "true"); call. You only need to invoke this once.

BrowserComponent Hierarchy

When Codename One packages applications into native apps it hides a lot of details to make the process simpler. One of the things hidden is the fact that we aren’t dealing with a JAR anymore, so getResource/getResourceAsStream are problematic…​ Both of these API’s support hierarchies and a concept of package relativity both of which might not be supported on all OS’s.

Codename One has its own getResourceAsSteam in the Display class and that works just fine, but it requires that all files be in the src root directory.

That’s why we recommend that you place files inside res files. A resource file allows you to add arbitrary data files and you can have as many resource files as you need.

For web developers this isn’t enough since hierarchies are used often to represent the various dependencies, this means that many links & references are relative. To work with such hierarchies just place all of your resources in a hierarchy under the html package in the project root. The build server will tar the entire content of that package and add an html.tar file into the native package. This tar is seamlessly extracted on the device when you actually need the resources and only with new application versions (not on every launch). So assuming the resources are under the html root package they can be displayed with code like this:

try {
    browserComponent.setURLHierarchy("/htmlFile.html");
} catch(IOException err) {
    ...
}

Notice that the path is relative to the html directory and starts with / but inside the HTML files you should use relative (not absolute) paths.

Also notice that an IOException can be thrown due to the process of untarring. Its unlikely to happen but is entirely possible.

NavigationCallback

At the core of the BrowserComponent we have the BrowserNavigationCallback. It might not seem like the most important interface within the browser but it is the "glue" that allows the JavaScript code to communicate back into the Java layer.

You can bind a BrowserNavigationCallback by invoking setBrowserNavigationCallback on the BrowserComponent. At that point with every navigation within the browser the callback will get invoked.

The shouldNavigate method from the BrowserNavigationCallback is invoked in a native thread and NOT ON THE EDT!
It is crucial that this method returns immediately and that it won’t do any changes on the UI.

The shouldNavigate indicates to the native code whether navigation should proceed or not. E.g. if a user clicks a specific link we might choose to do something in the Java code so we can just return false and block the navigation. We can invoke callSerially to do the actual task in the Java side.

Form hi = new Form("BrowserComponent", new BorderLayout());
BrowserComponent bc = new BrowserComponent();
bc.setPage( "<html lang=\"en\">\n" +
            "    <head>\n" +
            "        <meta charset=\"utf-8\">\n" +
            "        <script>\n" +
            "          function  fnc(message) {\n" +
            "         document.write(message);\n" +
            "            };\n" +
            "        </script>\n" +
            "    </head>\n" +
            "    <body >\n" +
            "        <p><a href=\"http://click\">Demo</a></p>\n" +
            "    </body>\n" +
            "</html>", null);
hi.add(BorderLayout.CENTER, bc);
bc.setBrowserNavigationCallback((url) -> {
    if(url.startsWith("http://click")) {
        Display.getInstance().callSerially(() -> bc.execute("fnc('<p>You clicked!</p>')"));
        return false;
    }
    return true;
});
Before the link is clicked for the
Figure 66. Before the link is clicked for the "shouldNavigate" call
After the link is clicked for the
Figure 67. After the link is clicked for the "shouldNavigate" call
The JavaScript Bridge is implemented on top of the BrowserNavigationCallback.

JavaScript

The JavaScript bridge is sometimes confused with the JavaScript Port. The JavaScript bridge allows us to communicate with JavaScript from Java (and visa versa). The JavaScript port allows you to compile the Codename One application into a JavaScript application that runs in a standard web browser without code changes (think GWT without source changes and with thread support).+ We discuss the JavaScript port further later in the guide.

BrowserComponent can communicate with the HTML code using JavaScript calls. E.g. we can create HTML like this:

Form hi = new Form("BrowserComponent", new BorderLayout());
BrowserComponent bc = new BrowserComponent();
bc.setPage( "<html lang=\"en\">\n" +
            "    <head>\n" +
            "        <meta charset=\"utf-8\">\n" +
            "        <script>\n" +
            "          function  fnc(message) {\n" +
            "         document.write(message);\n" +
            "            };\n" +
            "        </script>\n" +
            "    </head>\n" +
            "    <body >\n" +
            "        <p>Demo</p>\n" +
            "    </body>\n" +
            "</html>", null);
TextField tf = new TextField();
hi.add(BorderLayout.CENTER, bc).
        add(BorderLayout.SOUTH, tf);
bc.addWebEventListener("onLoad", (e) ->  bc.execute("fnc('<p>Hello World</p>')"));
tf.addActionListener((e) -> bc.execute("fnc('<p>" + tf.getText() +"</p>')"));
hi.show();
JavaScript code was invoked to append text into the browser image above
Figure 68. JavaScript code was invoked to append text into the browser image above
Notice that opening an alert in an embedded native browser might not work

We use the execute method above to execute custom JavaScript code. We also have an executeAndReturnString method that allows us to receive a response value from the JavaScript side.

Coupled with shouldNavigate we can effectively do everything which is exactly what the JavaScript Bridge tries to do.

The JavaScript Bridge

While it’s possible to just build everything on top of execute and shouldNavigate, both of these methods have their limits. That is why we introduced the javascript package, it allows you to communicate with JavaScript using intuitive code/syntax.

The JavascriptContext class lays the foundation by enabling you to call JavaScript code directly from Java. It provides automatic type conversion between Java and JavaScript types as follows:

Table 1. Java to JavaScript
Java Type Javascript Type

String

String

Double/Integer/Float/Long

Number

Boolean

Boolean

JSObject

Object

null

null

Other

Not Allowed

Table 2. JavaScript to Java
Javascript Type Java Type

String

String

Number

Double

Boolean

Boolean

Object

JSObject

Function

JSObject

Array

JSObject

null

null

undefined

null

This conversion table is more verbose than necessary, since JavaScript functions and arrays are, in fact Objects themselves, so those rows are redundant. All JavaScript objects are converted to JSObject.

We can access JavaScript variables easily from the context by using code like this:

Form hi = new Form("BrowserComponent", new BorderLayout());
BrowserComponent bc = new BrowserComponent();
bc.setPage( "<html lang=\"en\">\n" +
            "    <head>\n" +
            "        <meta charset=\"utf-8\">\n" +
            "    </head>\n" +
            "    <body >\n" +
            "        <p>This will appear twice...</p>\n" +
            "    </body>\n" +
            "</html>", null);
hi.add(BorderLayout.CENTER, bc);
bc.addWebEventListener("onLoad", (e) -> {
    // Create a Javascript context for this BrowserComponent
    JavascriptContext ctx = new JavascriptContext(bc);

    String pageContent = (String)ctx.get("document.body.innerHTML");
    hi.add(BorderLayout.SOUTH, pageContent);
    hi.revalidate();
});
hi.show();
The contents was copied from the DOM and placed in the south position of the form
Figure 69. The contents was copied from the DOM and placed in the south position of the form

Notice that when you work with numeric values or anything related to the types mentioned above your code must be aware of the typing. E.g. in this case the type is Double and not String:

Double outerWidth = (Double)ctx.get("window.outerWidth");

You can also query the context for objects and modify their value e.g.

Form hi = new Form("BrowserComponent", new BorderLayout());
BrowserComponent bc = new BrowserComponent();
bc.setPage( "<html lang=\"en\">\n" +
            "    <head>\n" +
            "        <meta charset=\"utf-8\">\n" +
            "    </head>\n" +
            "    <body >\n" +
            "        <p>Please Wait...</p>\n" +
            "    </body>\n" +
            "</html>", null);
hi.add(BorderLayout.CENTER, bc);
bc.addWebEventListener("onLoad", (e) -> {
    // Create a Javascript context for this BrowserComponent
    JavascriptContext ctx = new JavascriptContext(bc);

    JSObject jo = (JSObject)ctx.get("window");
    jo.set("location", "https://www.codenameone.com/");
});

This code effectively navigates to the Codename One home page by fetching the DOM’s window object and setting its location property to https://www.codenameone.com/.

Cordova/PhoneGap Integration

PhoneGap was one of the first web app packager tools in the market. It’s a tool that is effectively a browser component within a native wrapper coupled with native access API’s. Cordova is the open source extension of this popular project.

Codename One supports embedding PhoneGap/Cordova applications directly into Codename One applications. This is relatively easy to do with the BrowserComponent and JavaScript integration. The main aspect that this integration requires is support for Cordova plugins & its JavaScript API’s.

The effort to integrate Cordova/PhoneGap support into Codename One is handled within an open source github project here. The chief benefits of picking Codename One rather than using Cordova directly are:

  • Build Cloud

  • Better Native Code Support

  • Better Protection Of IP

  • IDE Integration Java - JavaScript - HTML

  • Easy, Doesn’t Require A Mac, Automates Certificates/Signing

  • Migration To Java

This is discussed further in the original announcement.

AutoCompleteTextField

The AutoCompleteTextField allows us to write text into a text field and select a completion entry from the list in a similar way to a search engine.

This is really easy to incorporate into your code, just replace your usage of TextField with AutoCompleteTextField and define the data that the autocomplete should work from. There is a default implementation that accepts a String array or a ListModel for completion strings, this can work well for a "small" set of thousands (or tens of thousands) of entries.

E.g. This is a trivial use case that can work well for smaller sample sizes:

Form hi = new Form("Auto Complete", new BoxLayout(BoxLayout.Y_AXIS));
AutoCompleteTextField ac = new AutoCompleteTextField("Short", "Shock", "Sholder", "Shrek");
ac.setMinimumElementsShownInPopup(5);
hi.add(ac);
Autocomplete Text Field
Figure 70. Autocomplete Text Field

However, if you wish to query a database or a web service you will need to derive the class and perform more advanced filtering by overriding the filter method:

public void showForm() {
  final DefaultListModel<String> options = new DefaultListModel<>();
  AutoCompleteTextField ac = new AutoCompleteTextField(options) {
      @Override
      protected boolean filter(String text) {
          if(text.length() == 0) {
              return false;
          }
          String[] l = searchLocations(text);
          if(l == null || l.length == 0) {
              return false;
          }

          options.removeAll();
          for(String s : l) {
              options.addItem(s);
          }
          return true;
      }

  };
  ac.setMinimumElementsShownInPopup(5);
  hi.add(ac);
  hi.add(new SpanLabel("This demo requires a valid google API key to be set below "
           + "you can get this key for the webservice (not the native key) by following the instructions here: "
           + "https://developers.google.com/places/web-service/get-api-key"));
  hi.add(apiKey);
  hi.getToolbar().addCommandToRightBar("Get Key", null, e -> Display.getInstance().execute("https://developers.google.com/places/web-service/get-api-key"));
  hi.show();
}

TextField apiKey = new TextField();

String[] searchLocations(String text) {
    try {
        if(text.length() > 0) {
            ConnectionRequest r = new ConnectionRequest();
            r.setPost(false);
            r.setUrl("https://maps.googleapis.com/maps/api/place/autocomplete/json");
            r.addArgument("key", apiKey.getText());
            r.addArgument("input", text);
            NetworkManager.getInstance().addToQueueAndWait(r);
            Map<String,Object> result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r.getResponseData()), "UTF-8"));
            String[] res = Result.fromContent(result).getAsStringArray("//description");
            return res;
        }
    } catch(Exception err) {
        Log.e(err);
    }
    return null;
}
Autocomplete Text Field with a webservice
Figure 71. Autocomplete Text Field with a webservice

Picker

Picker occupies the limbo between native widget and lightweight widget. Picker is more like TextField/TextArea in the sense that it’s a Codename One widget that calls the native code only during editing.

The reasoning for this is the highly native UX and functionality related to this widget type which should be quite obvious from the screenshots below.

At this time there are 4 types of pickers:

  • Time

  • Date & Time

  • Date

  • Strings

If a platform doesn’t support native pickers an internal fallback implementation is used. This is the implementation we always use in the simulator so assume different behavior when building for the device.

While Android supports Date, Time native pickers it doesn’t support the Date & Time native picker UX and will fallback in that case.

The sample below includes al picker types:

Form hi = new Form("Picker", new BoxLayout(BoxLayout.Y_AXIS));
Picker datePicker = new Picker();
datePicker.setType(Display.PICKER_TYPE_DATE);
Picker dateTimePicker = new Picker();
dateTimePicker.setType(Display.PICKER_TYPE_DATE_AND_TIME);
Picker timePicker = new Picker();
timePicker.setType(Display.PICKER_TYPE_TIME);
Picker stringPicker = new Picker();
stringPicker.setType(Display.PICKER_TYPE_STRINGS);

datePicker.setDate(new Date());
dateTimePicker.setDate(new Date());
timePicker.setTime(10 * 60); // 10:00AM = Minutes since midnight
stringPicker.setStrings("A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", "A Feast For Crows",
        "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring");
stringPicker.setSelectedString("A Game of Thrones");

hi.add(datePicker).add(dateTimePicker).add(timePicker).add(stringPicker);
hi.show();
The various picker components
Figure 72. The various picker components
The date & time picker on the simulator
Figure 73. The date & time picker on the simulator
The date picker component on the Android device
Figure 74. The date picker component on the Android device
Date & time picker on Android. Notice it didn't use a builtin widget since there is none
Figure 75. Date & time picker on Android. Notice it didn’t use a builtin widget since there is none
String picker on the native Android device
Figure 76. String picker on the native Android device
Time picker on the Android device
Figure 77. Time picker on the Android device

The text displayed by the picker on selection is generated automatically by the updateValue() method. You can override it to display a custom formatted value and call setText(String) with the correct display string.

A common use case is to format date values based on a specific appearance and Picker has builtin support for a custom display formatter. Just use the setFormatter(SimpleDateFormat) method and set the appearance for the field.

SwipeableContainer

The SwipeableContainer allows us to place a component such as a MultiButton on top of additional "options" that can be exposed by swiping the component to the side.

This swipe gesture is commonly used in touch interfaces to expose features such as delete, edit etc. It’s trivial to use this component by just determining the components placed on top and bottom (the revealed component).

SwipeableContainer swip = new SwipeableContainer(bottom, top);

We can combine some of the demos above including the Slider stars demo to rank GRRM’s books in an interactive way:

Form hi = new Form("Swipe", new BoxLayout(BoxLayout.Y_AXIS));
hi.add(createRankWidget("A Game of Thrones", "1996")).
    add(createRankWidget("A Clash Of Kings", "1998")).
    add(createRankWidget("A Storm Of Swords", "2000")).
    add(createRankWidget("A Feast For Crows", "2005")).
    add(createRankWidget("A Dance With Dragons", "2011")).
    add(createRankWidget("The Winds of Winter", "TBD")).
    add(createRankWidget("A Dream of Spring", "TBD"));
hi.show();

public SwipeableContainer createRankWidget(String title, String year) {
    MultiButton button = new MultiButton(title);
    button.setTextLine2(year);
    return new SwipeableContainer(FlowLayout.encloseCenterMiddle(createStarRankSlider()),
            button);
}
SwipableContainer showing a common use case of ranking on swipe
Figure 78. SwipableContainer showing a common use case of ranking on swipe

EmbeddedContainer

EmbeddedContainer solves a problem that exists only within the GUI builder and the class makes no sense outside of the context of the GUI builder. The necessity for EmbeddedContainer came about due to iPhone inspired designs that relied on tabs (iPhone style tabs at the bottom of the screen) where different features of the application are within a different tab.

This didn’t mesh well with the GUI builder navigation logic and so we needed to rethink some of it. We wanted to reuse GUI as much as possible while still enjoying the advantage of navigation being completely managed for me.

Android does this with Activities and the iPhone itself has a view controller, both approaches are problematic for Codename One. The problem is that you have what is effectively two incompatible hierarchies to mix and match.

The Component/Container hierarchy is powerful enough to represent such a UI but we needed a "marker" to indicate to the UIBuilder where a "root" component exists so navigation occurs only within the given "root". Here EmbeddedContainer comes into play, its a simple container that can only contain another GUI from the GUI builder. Nothing else. So we can place it in any form of UI and effectively have the UI change appropriately and navigation would default to "sensible values".

Navigation replaces the content of the embedded container; it finds the embedded container based on the component that broadcast the event. If you want to navigate manually just use the showContainer() method which accepts a component, you can give any component that is under the EmbeddedContainer you want to replace and Codename One will be smart enough to replace only that component.

The nice part about using the EmbeddedContainer is that the resulting UI can be very easily refactored to provide a more traditional form based UI without duplicating effort and can be easily adapted to a more tablet oriented UI (with a side bar) again without much effort.

MapComponent

The MapComponent uses a somewhat outdated tiling API which is not as rich as modern native maps. We recommend using the GoogleMap’s Native cn1lib to integrate native mapping functionality into the Codename One app.

The MapComponent uses the OpenStreetMap webservice by default to display a navigatable map.

The code was contributed by Roman Kamyk and was originally used for a LWUIT application.

Map Component
Figure 79. Map Component

The screenshot above was produced using the following code:

Form map = new Form("Map");
map.setLayout(new BorderLayout());
map.setScrollable(false);
final MapComponent mc = new MapComponent();

try {
   //get the current location from the Location API
   Location loc = LocationManager.getLocationManager().getCurrentLocation();

  Coord lastLocation = new Coord(loc.getLatitude(), loc.getLongtitude());
   Image i = Image.createImage("/blue_pin.png");
   PointsLayer pl = new PointsLayer();
   pl.setPointIcon(i);
   PointLayer p = new PointLayer(lastLocation, "You Are Here", i);
   p.setDisplayName(true);
   pl.addPoint(p);
   mc.addLayer(pl);
} catch (IOException ex) {
   ex.printStackTrace();
}
mc.zoomToLayers();

map.addComponent(BorderLayout.CENTER, mc);
map.addCommand(new BackCommand());
map.setBackCommand(new BackCommand());
map.show();

The example below shows how to integrate the MapComponent with the Google Location API. make sure to obtain your secret api key from the Google Location data API at: https://developers.google.com/maps/documentation/places/

MapComponent with Google Location API
Figure 80. MapComponent with Google Location API
          final Form map = new Form("Map");
           map.setLayout(new BorderLayout());
           map.setScrollable(false);
           final MapComponent mc = new MapComponent();
           Location loc = LocationManager.getLocationManager().getCurrentLocation();
           //use the code from above to show you on the map
           putMeOnMap(mc);
           map.addComponent(BorderLayout.CENTER, mc);
           map.addCommand(new BackCommand());
           map.setBackCommand(new BackCommand());

           ConnectionRequest req = new ConnectionRequest() {

               protected void readResponse(InputStream input) throws IOException {
                   JSONParser p = new JSONParser();
                   Hashtable h = p.parse(new InputStreamReader(input));
                   // "status" : "REQUEST_DENIED"
                   String response = (String)h.get("status");
                   if(response.equals("REQUEST_DENIED")){
                       System.out.println("make sure to obtain a key from "
                               + "https://developers.google.com/maps/documentation/places/");
                       progress.dispose();
                       Dialog.show("Info", "make sure to obtain an application key from "
                               + "google places api's"
                               , "Ok", null);
                       return;
                   }

                   final Vector v = (Vector) h.get("results");

                   Image im = Image.createImage("/red_pin.png");
                   PointsLayer pl = new PointsLayer();
                   pl.setPointIcon(im);
                   pl.addActionListener(new ActionListener() {

                       public void actionPerformed(ActionEvent evt) {
                           PointLayer p = (PointLayer) evt.getSource();
                           System.out.println("pressed " + p);

                           Dialog.show("Details", "" + p.getName(), "Ok", null);
                       }
                   });

                   for (int i = 0; i < v.size(); i++) {
                       Hashtable entry = (Hashtable) v.elementAt(i);
                       Hashtable geo = (Hashtable) entry.get("geometry");
                       Hashtable loc = (Hashtable) geo.get("location");
                       Double lat = (Double) loc.get("lat");
                       Double lng = (Double) loc.get("lng");
                       PointLayer point = new PointLayer(new Coord(lat.doubleValue(), lng.doubleValue()),
                               (String) entry.get("name"), null);
                       pl.addPoint(point);
                   }
                   progress.dispose();

                   mc.addLayer(pl);
                   map.show();
                   mc.zoomToLayers();

               }
           };
           req.setUrl("https://maps.googleapis.com/maps/api/place/search/json");
           req.setPost(false);
           req.addArgument("location", "" + loc.getLatitude() + "," + loc.getLongtitude());
           req.addArgument("radius", "500");
           req.addArgument("types", "food");
           req.addArgument("sensor", "false");

           //get your own key from https://developers.google.com/maps/documentation/places/
           //and replace it here.
           String key = "yourAPIKey";

           req.addArgument("key", key);

           NetworkManager.getInstance().addToQueue(req);
       }
       catch (IOException ex) {
           ex.printStackTrace();
       }
   }

Chart Component

The charts package enables Codename One developers to add charts and visualizations to their apps without having to include external libraries or embedding web views. We also wanted to harness the new features in the graphics pipeline to maximize performance.

Device Support

Since the charts package makes use of 2D transformations and shapes, it requires some of the graphics features that are not yet available on all platforms. Currently the following platforms are supported:

  1. Simulator

  2. Android

  3. iOS

Features

  1. Built-in support for many common types of charts including bar charts, line charts, stacked charts, scatter charts, pie charts and more.

  2. Pinch Zoom - The ChartComponent class includes optional pinch zoom support.

  3. Panning Support - The ChartComponent class includes optional support for panning.

Chart Types

The com.codename1.charts package includes models and renderers for many different types of charts. It is also extensible so that you can add your own chart types if required. The following screen shots demonstrate a small sampling of the types of charts that can be created.

Line Charts
Figure 81. Line Charts
Cubic Line Charts
Figure 82. Cubic Line Charts
Bar Charts
Figure 83. Bar Charts
Stacked Bar Charts
Figure 84. Stacked Bar Charts
Range Bar Charts
Figure 85. Range Bar Charts
Pie Charts
Figure 86. Pie Charts
Doughnut Charts
Figure 87. Doughnut Charts
Scatter Charts
Figure 88. Scatter Charts
Dial Charts
Figure 89. Dial Charts
Combined Charts
Figure 90. Combined Charts
Bubble Charts
Figure 91. Bubble Charts
Time Charts
Figure 92. Time Charts
The above screenshots were taken from the ChartsDemo app. You can start playing with this app by checking it out from our git repository.

How to Create A Chart

Adding a chart to your app involves four steps:

  1. Build the model. You can construct a model (aka data set) for the chart using one of the existing model classes in the com.codename1.charts.models package. Essentially, this is just where you add the data that you want to display.

  2. Set up a renderer. You can create a renderer for your chart using one of the existing renderer classes in the com.codename1.charts.renderers package. The renderer allows you to specify how the chart should look. E.g. the colors, fonts, styles, to use.

  3. Create the Chart View. Use one of the existing view classes in the com.codename1.charts.views package.

  4. Create a ChartComponent. In order to add your chart to the UI, you need to wrap it in a ChartComponent object.

You can check out the ChartsDemo app for specific examples, but here is a high level view of some code that creates a Pie Chart.

/**
 * Creates a renderer for the specified colors.
 */
private DefaultRenderer buildCategoryRenderer(int[] colors) {
    DefaultRenderer renderer = new DefaultRenderer();
    renderer.setLabelsTextSize(15);
    renderer.setLegendTextSize(15);
    renderer.setMargins(new int[]{20, 30, 15, 0});
    for (int color : colors) {
        SimpleSeriesRenderer r = new SimpleSeriesRenderer();
        r.setColor(color);
        renderer.addSeriesRenderer(r);
    }
    return renderer;
}

/**
 * Builds a category series using the provided values.
 *
 * @param titles the series titles
 * @param values the values
 * @return the category series
 */
protected CategorySeries buildCategoryDataset(String title, double[] values) {
    CategorySeries series = new CategorySeries(title);
    int k = 0;
    for (double value : values) {
        series.add("Project " + ++k, value);
    }

    return series;
}

public Form createPieChartForm() {

    // Generate the values
    double[] values = new double[]{12, 14, 11, 10, 19};

    // Set up the renderer
    int[] colors = new int[]{ColorUtil.BLUE, ColorUtil.GREEN, ColorUtil.MAGENTA, ColorUtil.YELLOW, ColorUtil.CYAN};
    DefaultRenderer renderer = buildCategoryRenderer(colors);
    renderer.setZoomButtonsVisible(true);
    renderer.setZoomEnabled(true);
    renderer.setChartTitleTextSize(20);
    renderer.setDisplayValues(true);
    renderer.setShowLabels(true);
    SimpleSeriesRenderer r = renderer.getSeriesRendererAt(0);
    r.setGradientEnabled(true);
    r.setGradientStart(0, ColorUtil.BLUE);
    r.setGradientStop(0, ColorUtil.GREEN);
    r.setHighlighted(true);

    // Create the chart ... pass the values and renderer to the chart object.
    PieChart chart = new PieChart(buildCategoryDataset("Project budget", values), renderer);

    // Wrap the chart in a Component so we can add it to a form
    ChartComponent c = new ChartComponent(chart);

    // Create a form and show it.
    Form f = new Form("Budget");
    f.setLayout(new BorderLayout());
    f.addComponent(BorderLayout.CENTER, c);
    return f;

}

Calendar

The Calendar class allows us to display a traditional calendar picker and optionally highlight days in various ways.

We normally recommend developers use the Picker UI rather than use the calendar to pick a date. It looks better on the devices.

Simple usage of the Calendar class looks something like this:

Form hi = new Form("Calendar", new BorderLayout());
Calendar cld = new Calendar();
cld.addActionListener((e) -> Log.p("You picked: " + new Date(cld.getSelectedDay())));
hi.add(BorderLayout.CENTER, cld);
The Calendar component
Figure 93. The Calendar component

ToastBar

The ToastBar class allows us to display none-obtrusive status messages to the user at the bottom of the screen. This is useful for such things as informing the user of a long-running task (like downloading a file in the background), or popping up an error message that doesn’t require a response from the user.

Simple usage of the ToastBar class looks something like this:

Status status = ToastBar.getInstance().createStatus();
status.setMessage("Downloading your file...");
status.show();

//  ... Later on when download completes
status.clear();
ToastBar with message
Figure 94. ToastBar with message

We can show a progress indicator in the ToastBar like this:

Status status = ToastBar.getInstance().createStatus();
status.setMessage("Hello world");
status.setShowProgressIndicator(true);
status.show();
Status Message with Progress Bar
Figure 95. Status Message with Progress Bar

We can automatically clear a status message/progress after a timeout using the setExpires method as such:

Status status = ToastBar.getInstance().createStatus();
status.setMessage("Hello world");
status.setExpires(3000);  // only show the status for 3 seconds, then have it automatically clear
status.show();

We can also delay the showing of the status message using showDelayed as such:

Status status = ToastBar.getInstance().createStatus();
status.setMessage("Hello world");
status.showDelayed(300); // Wait 300 ms to show the status

// ... Some time later, clear the status... this may be before it shows at all
status.clear();
ToastBar with a multiline message
Figure 96. ToastBar with a multiline message

SignatureComponent

The SignatureComponent provides a widget that allows users to draw their signature in the app.

Simple usage of the SignatureComponent class looks like:

Form hi = new Form("Signature Component");
hi.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
hi.add("Enter Your Name:");
hi.add(new TextField());
hi.add("Signature:");
SignatureComponent sig = new SignatureComponent();
sig.addActionListener((evt)-> {
    System.out.println("The signature was changed");
    Image img = sig.getSignatureImage();
    // Now we can do whatever we want with the image of this signature.
});
hi.addComponent(sig);
hi.show();
The Signature Component
Figure 97. The signature Component

Accordion

The Accordion displays collapsible content panels.

Simple usage of the Accordion class looks like:

Form f = new Form("Accordion", new BoxLayout(BoxLayout.Y_AXIS));
f.setScrollableY(true);
Accordion accr = new Accordion();
accr.addContent("Item1", new SpanLabel("The quick brown fox jumps over the lazy dog\n"
        + "The quick brown fox jumps over the lazy dog"));
accr.addContent("Item2", new SpanLabel("The quick brown fox jumps over the lazy dog\n"
        + "The quick brown fox jumps over the lazy dog\n "
        + "The quick brown fox jumps over the lazy dog\n "
        + "The quick brown fox jumps over the lazy dog\n "
        + ""));

accr.addContent("Item3", BoxLayout.encloseY(new Label("Label"), new TextField(), new Button("Button"), new CheckBox("CheckBox")));

f.add(accr);
f.show();
The Accordion Component
Figure 98. The Accordion Component

Floating Hint

FloatingHint wraps a text component with a special container that can animate the hint label into a title label when the text component is edited or has content within it.

Form hi = new Form("Floating Hint", BoxLayout.y());
TextField first = new TextField("", "First Field");
TextField second = new TextField("", "Second Field");
hi.add(new FloatingHint(first)).
        add(new FloatingHint(second)).
        add(new Button("Go"));
hi.show();
The FloatingHint component with one component that contains text and another that doesn't
Figure 99. The FloatingHint component with one component that contains text and another that doesn’t

1. String width is the real expensive part here, the complexity of font kerning and the recursion required to reflow text is a big performance hurdle
2. Image by RegisFrey - Own work, Public Domain, https://commons.wikimedia.org/w/index.php?curid=10298177
3. Fisheye is an effect where the selection stays in place as the list moves around it
5. The text below is from A Wiki of Ice & Fire: http://awoiaf.westeros.org/index.php/A_Game_of_Thrones