This section shows how to create lists with custom features and patterns in LWUIT for various use cases. For example, the Exclusive list and Multiselection list are used for selecting items instead of drilling down to the next view. Note that some of these Lists have been implemented using alternative techniques and components, such as RadioButton, CheckBox, and TextArea, in order to achieve user patterns compliant with the Series 40 platform UX.
These lists are implemented in the ListDemo example application, available in the <Nokia SDK 2.0 for Java installation directory>\plugins\lwuit\examples directory.
Figure: List with image icons
The LWUIT List component with the default list cell renderer can display image icons in addition to text. To display images, populate the ListModel with Commands containing both the text and the image.
ListModel model = new DefaultListModel(); for (int i = 0; i < 10; i++) { String text = “List item ” + (i + 1); Command item = new Command(text, loadImage("/thumbnail_" + (i + 1) + ".png")); model.addItem(item); } // Create a list based on the model list = new List(model);
Figure: Exclusive list
Exclusive list is a type of list for making exclusive selections. The list items are presented as labeled radio buttons. The selected list item has its radio button toggled.
Exclusive list can be implemented by adding multiple RadioButton components to a Form with vertical BoxLayout. You can set the appearance of the RadioButton to be like list item by setting its UIID to “ListItem” with setUIID() method. This will change the focus highlight color, borders and other UI attributes of the RadioButton component to be like the ones of a list item.
The RadioButtons are then added to a ButtonGroup. Creating a set of RadioButtons with the same ButtonGroup object means that only one RadioButton can be selected among the ButtonGroup.
The reason for using RadioButtons instead of the LWUIT List with a custom ListCellRenderer is that the LWUIT List selection is changed on press events immediately when the screen is touched. In exclusive list you would see radio buttons getting toggled each time you touch the screen for scrolling the list. When using RadioButton components, the touch events are handled properly and the user can scroll the view without changing the selection.
Form form = new Form(); form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); radioButtonGroup = new ButtonGroup(); for (int i = 0; i < 10; i++) { RadioButton radioButton = new RadioButton(“List item ” + (i + 1)); radioButton.setUIID("ListItem"); radioButtonGroup.add(radioButton); form.addComponent(radioButton); radioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { // Code for handling the list selection } }); }
Figure: Exclusive list with confirmation
This list type is similar to the exclusive list described in the previous section, except in this case the list selection can be changed multiple times until the selection is explicitly confirmed by pressing the default command. The code that handles the list selection is added to the ActionListener of the default command instead of the RadioButtons.
Form form = new Form(); form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); radioButtonGroup = new ButtonGroup(); for (int i = 0; i < 10; i++) { RadioButton radioButton = new RadioButton(“List item” + (i + 1)); radioButton.setUIID("ListItem"); radioButtonGroup.add(radioButton); form.addComponent(radioButton); } radioButtonGroup.setSelected(0); // Create and add Command for confirming the selection Command doneCommand = new Command(("Done") { public void actionPerformed(ActionEvent e) { // Handle list selection here } }; form.setDefaultCommand(doneCommand); form.addCommand(doneCommand);
Figure: Multiselection list
The multiselection list allows the user to select multiple options at once by toggling list items on and off. The list is implemented like the Exclusive List with Confirmation described in the previous section, but CheckBoxes are used instead of RadioButtons.
Form form = new Form(); form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); for (int i = 0; i < 10; i++) { CheckBox checkBox = new CheckBox(“List item” + (i + 1)); checkBox.setUIID("ListItem"); addComponent(i, checkBox); } // Create and add Command for confirming the selection Command doneCommand = new Command(“Done”) { public void actionPerformed(ActionEvent e) { // Handle list selection here } }; // Set default command only on Full Touch targets, so the Command won’t override the list selection MSK item if (Display.getInstance().isTouchScreenDevice()) { form.setDefaultCommand(doneCommand); } form.addCommand(doneCommand);
Figure: Wrapped list
Text wrapping can be implemented with TextAreas. The TextArea components are put inside Containers that are added to a Form with vertical BoxLayout. To make the TextAreas act as pressable and focusable list items, the Containers delegate their events to Button components by using setLeadComponent method. The Buttons are not visible. They are only used as a delegate for handling the list selection and focus traversal for the TextAreas.
Here the ListItem class wraps the TextArea inside a Container and sets the lead component:
public static class ListItem extends Container { private final Button leadComponent = new Button(); public ListItem(String text, ActionListener listener) { setLayout(new BorderLayout()); setUIID("ListItem"); leadComponent.addActionListener(listener); // Add leadComponent to the container to gain traversal functionality addComponent(BorderLayout.NORTH, leadComponent); leadComponent.setVisible(false); setLeadComponent(leadComponent); // Create the TextArea and add it to the Container TextArea textArea = new TextArea(); textArea.setText(text); textArea.setUIID("ListItem"); textArea.setEditable(false); textArea.setRows(2); textArea.setGrowByContent(true); addComponent(BorderLayout.CENTER, textArea); } /** * Capture fire clicked event, so that lead component can be notified. * Cannot call leadComponents fireClicked directly, but this is what * Button would basically do. This would happen automatically in an * ideal world. */ protected void fireClicked() { leadComponent.pressed(); leadComponent.released(); } }
The Form is then populated with ListItems:
for (int i = 0; i < 10; i++) { final int index = i; String text = “List item” + (i + 1) + “, Lorem ipsum…”; ListItem listItem = new ListItem(text, new ActionListener() { public void actionPerformed(ActionEvent ae) { // Handle list selection } }); form.addComponent(listItem); }
Figure: Truncated list
If your list items contain long Strings that are wider than the screen width, you can have them truncated to fit the screen and the truncated part replaced with ellipsis (...). If you are using LWUIT DefaultListCellRenderer, use its method setEndsWith3Points(true) to enable truncation.
DefaultListCellRenderer renderer = new DefaultListCellRenderer(); renderer.setEndsWith3Points(true); list.setRenderer(renderer);
If your list contains many truncated list items, it is recommended to use NokiaListCellRenderer which optimises the truncation by caching the information needed when truncating the Strings. You can use it in place of the DefaultListCellRenderer. NokiaListCellRenderer always has the truncation enabled.
NokiaListCellRenderer renderer = new NokiaListCellRenderer(); list.setRenderer(renderer);
Figure: List with list items having title and subtext
This type of list is seen frequently in native Series 40 full touch UI. The list item can contain a descriptive text in addition to a thumbnail and main text. While LWUIT does not directly support this type of list, such list can be implemented by creating a custom list item model and list cell renderer. The custom list item model holds the title, subtext and Image data of a list item, and the custom list cell renderer handles the drawing of the list items.
A code sample of a custom list item model for list items having title and subtext:
public class TwoRowListItem extends DefaultListModel { private String title; private String text; private Image icon; public TwoRowListItem(String title, String text, Image icon) { this.title = title; this.text = text; this.icon = icon; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Image getIcon() { return icon; } public void setIcon(Image icon) { this.icon = icon; } }
The list item instances are created and added to a ListModel as usual:
ListModel model = new DefaultListModel(); for (int i = 0; i < 10; i++) { String text = “List item” + (i + 1); String subText = "Info"; Image image = loadImage("/thumbnail_" + (i + 1) + ".png"); TwoRowListItem item = new TwoRowListItem(text, subText, image); model.addItem(item); }
Then the list is created based on the model.
list = new List(model);
A code sample of a custom list cell renderer for list items having title and subtext:
public class TwoRowListCellRenderer extends Component implements ListCellRenderer { private static TwoRowListCellRenderer instance; private String mainText; private String subText; private Font mainFont = Font.createSystemFont( Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); private Font subFont = Font.createSystemFont( Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL); private Image image; static { instance = new TwoRowListCellRenderer(); instance.setUIID("ListItem"); } private void setMainText(String text) { mainText = text; } private void setSubText(String text) { subText = text; } private void setIcon(Image image) { this.image = image; } public Component getListCellRendererComponent(List list, Object o, int index, boolean selected) { if (o instanceof TwoRowListItem) { TwoRowListItem item = (TwoRowListItem) o; instance.setMainText(item.getTitle()); instance.setSubText(item.getText()); instance.setIcon(item.getIcon()); instance.setFocus(selected); } return instance; } public Component getListFocusComponent(List list) { return instance; } public void paint(Graphics g) { super.paint(g); UIManager.getInstance().getLookAndFeel().setFG(g, this); boolean rtl = this.isRTL(); Style style = getStyle(); int leftPadding = style.getPadding(rtl, Component.LEFT); int topPadding = style.getPadding(rtl, Component.TOP); int imgY = getY() + (getHeight() - image.getHeight()) / 2; g.drawImage(image, getX() + leftPadding, imgY); leftPadding += leftPadding + image.getWidth(); g.setFont(mainFont); g.drawString(mainText, getX() + leftPadding, getY() + topPadding); int yOffset = mainFont.getHeight(); g.setFont(subFont); g.drawString(subText, getX() + leftPadding, getY() + topPadding + yOffset); } public Dimension calcPreferredSize() { int textHeight = mainFont.getHeight() + subFont.getHeight(); int height = Math.max(textHeight, image.getHeight()); return new Dimension(50, height + getStyle().getPadding(false, Component.TOP) + getStyle().getPadding(false, Component.BOTTOM)); } }
The renderer gets instantiated and added to the List. Note that the renderer uses a single Component to draw all list items. The renderer is bit like a stamp that gets modified between “stamping” the subsequent list items.
ListCellRenderer renderer = new TwoRowListCellRenderer(); list.setRenderer(renderer);