Sunday, July 6, 2008

LWUIT List Renderer, by Chen Fishbein

List is one of the most important widgets in LWUIT, we also discovered this is one of the most difficult Widget to understand.

In this post I will review the List Renderer to give you a better understanding what you can achive when you create your own Renderer to the List.

When we designed this widget we took the MVC separation model from Swing, that means we created a data model to encapsulate the data and a renderer to display the data items to the screen.

The renderer:
Let's have a closer look at the List 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.
public Component getListFocusComponent(List list);

}
Now let's try to implement our own renderer's.
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 moves on the List, this is just because the renderer returns a Label with the same style regardless if it's being 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.getStyle().setBgTransparency(100);
} else {
l.setFocus(false);
l.getStyle().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 gives the focus to the Label,
it is simply indicates to the LookAndFeel to draw the Label with fgSelectionColor and bgSelectionColor instead of fgColor and bgColor.
Then we call to Label.getStyle().setBgTransparency(100) to give the selection semi transparency and 0 for full transparency if not selected.
OK that's a bit more functional, but not very efficient that's because we create a new Label each time the method is called.

To make it more device friendly keep a reference to the Component or extend the Widget.

class MyRenderer extends Label{

public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){
setText(value.toString());
if (isSelected) {
setFocus(true);
getStyle().setBgTransparency(100);
} else {
setFocus(false);
getStyle().setBgTransparency(0);
}
return this;
}
}
}
Now Let's have a look at a more advanced Renderer (This was taken from the LWUIT Scrolling Demo)
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.getStyle().setBgTransparency(0);
name.getStyle().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
email.getStyle().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 renderer a Contact Object to the Screen, we build the Component in the constructor and in the getListCellRendererComponent we simply updates the Labels texts according to the Contact Object.
Notice that in this renderer I return a focus Label with semi transparency, as mentioned before the focus component can be modified by using this method.
For example I can modify the focus Component to have an icon.
    focus.getStyle().setBgTransparency(100);
try {
focus.setIcon(Image.createImage("/duke.png"));
focus.setAlignment(Component.RIGHT);
} catch (IOException ex) {
ex.printStackTrace();
}


In the next post I will review the List Model

Chen

53 comments:

  1. Hello Shai and Chen,
    How about doing a Label ticker in the list above, say the name is too long. I've done the label ticker in a Form by simply calling its startTicker method, but when i do that in the getListCellRendererComponent method of the ListCellRenderer, I get an IllegalArgumentException. Is this the right place to that? A code snippets will be of great help. Thanks!

    ReplyDelete
  2. You mean this:
    http://lwuit.blogspot.com/2008/06/implementing-selected-item-ticker-in.html

    ReplyDelete
  3. Thanks for the reply. I already saw your posted link before. Sorry to confuse you on what I'm trying to do. Since the Million Contacts March consists of several controls per list item, your link in its sense, won't work. I used the label.setShiftText in the getListCellRendererComponent method instead.

    But now I'm facing a slight problem- I can't get the ticker work like that of your label implementation on a form (say label.startTicker(100, true);), which scrolls (on the second pass) starting from the end of the screen when its last character is not visible anymore. Mine always start from 0 position. Here's my code snippet:

    int titleWidth = container.getStyle().getFont().stringWidth(title.getText());
    int visibleWidth = container.getLayoutWidth();
    if (isSelected && (titleWidth > visibleWidth))
    title.setShiftText(-(position % (title.getText().length() * 6)));
    else
    title.setShiftText(0);


    position changes its value in list's animate method (just like your code). Thanks again!

    ReplyDelete
  4. Hello Shai,

    This is a great blog and answered all my queries on list except this one. In the LWUIT Demo - Scroll Demo Application I scroll in the List to the last record. But when I want to come scroll up from the last record, the list does not scroll. Is there some property for the same. Also I want to make the list Scroll to the first record from the last record. Is there any property that can help me with this.

    Regards,
    Shubham

    ReplyDelete
  5. I don't understand the first question, check that you are using the latest version from SVN.
    For cyclic scrolling use the fixed cyclic mode of the list.

    ReplyDelete
  6. Nice one, I just updated my LWUIT from SVN and I just tried creating a simple list and adding 100 labels to it. The list doesn't seem to scroll anymore.

    ReplyDelete
  7. Ok my bad was too impatient. I did not read the million entry thingy about how the list work. I guess i need a list model.

    ReplyDelete
  8. You don't need a model if your list doesn't scroll. Most likely you placed it in a scrollable form and didn't use a layout such as border layout. Please search the forum for this question, it is answered repeatedly.

    ReplyDelete
  9. thx did not use cell renderer as well so obviously no painting.

    ReplyDelete
  10. Hi there. I was wondering, I need the list to autoscroll when new items are being populated to the list, so I have set the list to FIXED_TRAIL when the

    list.getScrollDimension().getHeight() > list.getHeight() which I set to 7/10 of the screen size using the setPreferedHeight method.

    This is my problem: Everything seems to go smoothly but once the list.getDimension.getHeight > list.getheight, it seems that the first element that was previously entered appears and stays at the bottom of the list. Is this an expected behaviour?

    Many thanks.

    ReplyDelete
  11. hi Shai and Chen,

    i dnt get this one.
    i want to add a Image to a list.
    but List expects an Object.
    so when i add a image to a list the hashcode of the image eill displayed.

    how can i sort this out???
    regards,
    Randika

    ReplyDelete
  12. I'm trying to implement the thumbnail image on the focused item on the list. The thumbnail image have height bigger than list item height (the image is sliced to the height of the row), so how can I set the height of the focused item bigger and get displayed all the immage?

    ReplyDelete
  13. I'm trying to implement the thumbnail image on the focused item on the list. The thumbnail image have height bigger than list item height (the image is sliced to the height of the row), so how can I set the height of the focused item bigger and get displayed all the immage?

    ReplyDelete
  14. @me4tatel See the renderer demo in LWUIT demo, select the Fisheye demo.

    ReplyDelete
  15. @saini add an action listener to the list and in it change the state of the MODEL to reflect selection. When rendering use that state to invoke checkbox.setSelected.

    ReplyDelete
  16. What if the images in the Million Contacts are animated gifs ? Animated gifs look static when put inside a List. Is there any way around this ?
    Thanks

    ReplyDelete
  17. @The.JagaL: animated renderers are really mean the list should be animated and it should handle that. See the ticker post for examples of that...

    ReplyDelete
  18. Makes sense. But it actually only animates the currently selected item in the list. What if I need to animate an unselected item ?
    Thanks

    ReplyDelete
  19. I think I figured it out. I just had to override the list's animate method in addition to the renderer's animate method, unless there's an easier way ?

    ReplyDelete
  20. You can disable all visual effects using the LookAndFeel/Form classes

    ReplyDelete
  21. Hi!
    First, thanks you for this tuto.
    I have a question, how can I add buttons to the list and propagate touch/keys events?

    Thanks and best regards!

    ReplyDelete
  22. u can just use a normal form for that . No need for a list.

    ReplyDelete
  23. i want to add a set of buttons to a single tab of a Tabbedpane.so i am planning to add all these buttons to a list n then add this list to a particular tab.
    when i do this....i am displayed some internal representation of the button on the form. i have to override the toString() method.but i dont know where i have to do it.
    can anybody help me plzz.

    ReplyDelete
  24. @Swetha: read the post.

    @DX: Try the UI demo, it performs fine on 5800 and has two labels and an image. Check your theme/images to see which is causing the slow down.

    ReplyDelete
  25. Hello all,i'm having a problem with scrolls in lwuit...
    I have added a list to a form but when i press the down-key of a (non-touch phone) the scroll goes towards right side and shows me the last-part of the content of list and when i press the left key to view the start part of the list content,it doesnt go there.....what do i do?

    ReplyDelete
  26. I'm not sure what you are trying to describe. Try using the next focus left/right/up/down methods of component.

    ReplyDelete
  27. Hey bro,
    In my form, there is first a label.. with an image, and then a container which has the list, but when i scroll down the list and come back up, the label won't show. It just vanishes. Can u help me please?

    ReplyDelete
  28. Its hard to tell without understanding the full details. Make sure you are using the latest LWUIT.
    Try to reproduce this by adding a label to the north of the LWUIT demo scroll demo. Assuming it doesn't reproduce see the difference between that and your code.

    ReplyDelete
  29. Hi i have implement listrender with image and name like now i have to any one of the select list of item it should display another list can any one give solution for this

    ReplyDelete
  30. pls how to implement listcellrender have another list can you pls guide me how to do this

    ReplyDelete
  31. Look at the LWUIT Demo scroll and renderer code.

    ReplyDelete
  32. Hi shai,

    I got a problem with the list.

    I am generating a checkbox list dynamically.i use a use a separate class to store the state(selected or not) and the name of the checkbox.

    i make an array out of the class an make it a model.

    but when i make changes to array elements, how i can automatically refresh the list.ryt now the list react to earlier changes after i make a new change in the list..(Here i'm referring to selecting or unselecting a checkbox as a change )

    How can i make the list auto refresh?..

    pls reply..

    ReplyDelete
  33. When changing the model it should fire the model change event (see the default list model code) thus notifying the list it needs a refresh.
    The list will request the renderer over again which you can mark as selected/unselected.

    ReplyDelete
  34. I built a container list with 3 labels and 1 button. the button doesnt work. I looked around I didn't find anything useful for me. Please help me out

    ReplyDelete
  35. Ok, so how can I use button or checkbox anyway?

    ReplyDelete
  36. hey can you plz give me a simple list code with left side image and right side text
    with implantation code

    ReplyDelete
  37. And one more problem i am facing
    when i remove all item and ad label with waiting text and send http request.
    form dose not change until http request complete
    means i cannot implement wait form ..

    ReplyDelete
  38. Look at the LWUITDemo for a sample.
    You are blocking the EDT on networking, use a thread and read up on the LWUIT EDT in the developer guide.

    ReplyDelete
  39. This comment has been removed by the author.

    ReplyDelete
  40. hey can you give me sample code what to put before sending http request to
    update my form i m using single thread app

    i dont understand how to use Display methods callSerially, callSeriallyAndWait, and
    invokeAndBlock etc..

    actually i want to make Login form while sending authentication request i just want to show waiting image and text ..
    help me to do so

    ReplyDelete
  41. i got the answer thanks ...

    but still unable to to make simple list with image left side and text right side

    ReplyDelete
  42. done with image list ....
    Thanks for advise

    ReplyDelete
  43. Hi, I have problems on the ComboBox popup list theming. I've tried some component but nothing seems to work. How can I change the theme of those ComboBox popups?

    ReplyDelete
  44. Murugan

    Hello,Am getting Name and number from server database ,That one i want make it as a list view for example like contact viewing name and number know like that -please any one help me.
    Thanks.

    ReplyDelete
  45. Hi Shai,

    I'm having a problem in displaying very long lists(30 entries or more). On the screen, the list gets displayed in a compressed area. it looks like this.
    ---------------
    item6
    item7
    item8
    item9
    item..
    item..
    item..
    item..
    item..
    -------------
    and the list consumes only half of the screen.
    Im using the 'wood' theme in resource editor

    ReplyDelete
  46. can anyone create this example in a midlet so i can run and test this. i can't get any of this. i'm new to this field. i need a interface exactly like this for an assessment. please paste the code here.

    ReplyDelete
  47. can anyone explain to me where the class Contact is initialized? how if i want to create a list with 3 labels and 2 images inside every list item?

    ReplyDelete
  48. @ Herahadi, am also wondering the same thing. could someone please explain where the Contact object is declared and initialized?

    ReplyDelete
  49. This comment has been removed by the author.

    ReplyDelete