Wednesday, October 14, 2009

Tickering Everywhere

Its been a while since my original LWUIT ticker list post and quite a few has changed, back when I originally posted there was no built in tickering functionality in LWUIT itself. Recent questions in the mailing list and from partners prompted me to write the code bellow, generally for tickering a ComboBox however I tried to make it as generic as possible so it can be installed into every list out there.

Notice that to ticker custom components you need to set them as a cell renderer to prevent the label from throwing an exception when invoking startTicker. You can use the TickerRenderer bellow with practically every list and it doesn't need to be a part of a combo box.

public class TickerComboDemo extends MIDlet {
public void startApp() {
try {
Display.init(this);
Resources r = Resources.open("/javaTheme.res");
UIManager.getInstance().setThemeProps(r.getTheme(r.getThemeResourceNames()[0]));
Form form = new Form("Ticker Combo");
ComboBox combo = new ComboBox(new String[]{"Jack",
"Name that should probably trigger a ticker",
"Another name that should probably trigger a ticker",
"Kate", "Sawyer", "Sayid", "Hurley", "Jin", "Sun", "Charlie", "Claire",
"Aaron"
, "Michael", "Walt", "Boone", "Shannon", "Locke", "Mr. Eko",
"Ana-Lucia", "Libby", "Desmond", "Benjamin Linus", "Juliet Burke"}) {
protected List createPopupList() {
List l = super.createPopupList();
l.setListCellRenderer(new TickerRenderer());
return l;
}
};
form.addComponent(combo);
form.show();
} catch (IOException ex) {
ex.printStackTrace();
}
}

class TickerRenderer extends DefaultListCellRenderer {
private DefaultListCellRenderer selectedRenderer = new DefaultListCellRenderer(false);
private List parentList;
public TickerRenderer() {
super(false);
}

public boolean animate() {
if(parentList != null && parentList.getComponentForm() != null) {
if(selectedRenderer.isTickerRunning()) {
if(selectedRenderer.animate()) {
parentList.repaint();
}
}
}
return super.animate();
}

public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
if(isSelected) {
selectedRenderer.getListCellRendererComponent(list, value, index, isSelected);

// sometimes the list asks for a dummy selected value for size calculations and this might
// break the tickering state
if(index == list.getSelectedIndex()) {
if(selectedRenderer.shouldTickerStart()) {
if(!selectedRenderer.isTickerRunning()) {
parentList = list;
list.getComponentForm().registerAnimated(this);
selectedRenderer.startTicker(UIManager.getInstance().getLookAndFeel().getTickerSpeed(), true);
}
} else {
if(selectedRenderer.isTickerRunning()) {
selectedRenderer.stopTicker();
}
}
}
return selectedRenderer;
} else {
return super.getListCellRendererComponent(list, value, index, isSelected);
}
}
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

19 comments:

  1. You need to create a TextArea object for each element in your array.

    ReplyDelete
  2. Hi, I don't understand how to do a simply List with Lwuit, I see the process very complex, and I have read a lot of examples and documentation but I don't reach the point.
    This examples don't work in my application. Could you explain me the basics, please?

    ReplyDelete
  3. @Sunny Peng: Bitmap fonts are impractical for the full Chinese alphabet because of the memory requirements (they are heavy on RAM) we recommend CJK speakers use system fonts (you can use bitmap fonts for hardcoded strings and only encode a very limited charset but that might produce an "uneven" experience).

    We currently don't support underline since the MIDP approach to underlining conflicts with its support on other platforms. We will probably include something in a future version though.

    @Jwarrior: Try something similar to this http://lwuit.blogspot.com/2009/10/tickering-everywhere.html

    ReplyDelete
  4. Hi Shai,

    I'm try to figure out how to port your TickerRender in another ListCellRenderer.
    Note that I need to expose 2 labels, one of them nedd to be a ticker that start only when the container of the label is selected. I tried with this method but this seems to not work properly and all the label called "address" start to tiker.
    What i can do?

    public class RouteCreatorListRenderer extends Container implements ListCellRenderer {

    private Label address = new Label("");
    private Label label = new Label("");
    private static Border b = Border.createLineBorder(4, 0x99cc00);

    public RouteCreatorListRenderer(){
    setLayout(new BoxLayout(BoxLayout.Y_AXIS));
    addComponent(label);
    addComponent(address);
    }

    private List parentList;

    public boolean animate() {
    if(parentList != null && parentList.getComponentForm() != null) {
    if(address.isTickerRunning()) {
    if(address.animate()) {
    parentList.repaint();
    }
    }
    }
    return super.animate();
    }

    public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
    address.setText("testtttttttttttttttttttxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxt");
    label.setText("label");

    if(isSelected) {
    getStyle().setBorder(b);

    // sometimes the list asks for a dummy selected value for size calculations and this might
    // break the tickering state
    if(index == list.getSelectedIndex()) {
    if(address.shouldTickerStart()) {
    if(!address.isTickerRunning()) {
    parentList = list;
    list.getComponentForm().registerAnimated(this);
    address.startTicker(UIManager.getInstance().getLookAndFeel().getTickerSpeed(), true);
    }
    } else {
    if(address.isTickerRunning()) {
    address.stopTicker();
    }
    }
    }
    return this;
    } else {
    getStyle().setBorder(null);
    return this;
    }
    }

    public Component getListFocusComponent(List list) {
    // TODO Auto-generated method stub
    return null;
    }

    }

    ReplyDelete
  5. @Marco: Return a different instance of the renderer for the selected/unselected entries. Start the ticker only on the selected entries and never on the unselected entries.

    ReplyDelete
  6. Hi there,
    Are there any decent samples or examples out there of a custom cell renderer that just has a few labels stacked vertically on top of each other? (I presume a single Label doesn't handle newlines...)

    Also, if I want a Label that runs off the edge of the screen to have ellipsis "..." at the right hand edge (to show that it has been truncated), how do I do that? Do I make the label a ticker, and just not start it 'tickering' so it is static?

    Thanks! LWUIT looks fab!

    ReplyDelete
  7. There are samples of renderers in the UI Demo, Makeover demo and scattered across the forum not to mention the tutorial and developer guide.

    ReplyDelete
  8. Hi Shai,

    In the TickerRenderer example above I’ve noticed the following behavior in a list with multiple items that require the ticker. The ticker starts from the position where the previous selected item’s ticker was stopped and not at beginning of the current selected item (i.e list item text positions 0).

    Is there a way to always start the ticker at the beginning of the current selected item? (i.e always start ticker at text position 0 for the selected list item)

    Thanks,

    Tarun

    ReplyDelete
  9. Sure add a selection listener to the list and set the tickering offset to zero when selection changes.

    ReplyDelete
  10. Shai,

    How does one access the tickering offset? Are there any samples or examples that you can suggest I look at, which show how to set it to zero?

    Thanks

    ReplyDelete
  11. Hello Shai! I want to follow up what tarun has just posted: How can we access the tickering offset to be able to set it to zero? Sample please, thanks!

    Lastly, did you notice something like a shadow in the background when traversing a list using this TickerRenderer? Further experimentation in the Theme Creator revealed that the two components (supposedly be one only, I guess) are the ListRenderer and the ListRendererFocus. So I set the same bgcolor for them. But what disatisfy me is ListRendererFocus lags behind when you move down or up on the list. Is there a way to fix this?

    Thank you very much and more power. Long live LWUIT!

    ReplyDelete
  12. Use set/getShiftText for newer versions of LWUIT.

    The "shadow" is the smooth scrolling effect returned by the focus component in the renderer. If its not smooth you need to optimize your renderer/theme.

    ReplyDelete
  13. This is very good renderer
    thank you very much

    ReplyDelete
  14. Hello Shai!
    I'm braziliam programmer and i'm so glad to know that LWUIT will be used in our DTV system! I have an issue with this Ticker Renderer: i want to stop the tickering every time i click in an item, that start tickering in the beginning, not in the point the previous item stoped. Are you understandig? Thanks a lot, since already.

    ReplyDelete
  15. hello Shai, i got it! I just used in ticker renderer a lastIndex var, and set ticker to position zero with setShiftText method all times lastIndex != index. Thanks!

    ReplyDelete
  16. Hi Shai

    I have Created A custom button component in that i have added two label. In that i want to ticker the first label i haved tried to start the ticker for label but it wont work.
    i simply added these button in the form on gaining the focus i want to start the ticker.

    can you please tell me the brief about the method animate() in LWUIT.

    Thanks and Waiting For the reply soon.

    ReplyDelete
  17. You need to delegate to the subcomponents.
    I suggest you take a look at how the new GenericListCellRenderer does this.

    ReplyDelete
  18. I have used the combobox in form and there in no central key in mobile so how can I click the combobox so that the list of items of combobox would be display?

    Is there any method for selecting the items of combobox without using the central key(which is not available in my mobile)?

    ReplyDelete
  19. By default you should get "select/cancel" softbuttons for the combo box see: setIncludeSelectCancel in conbo box.

    ReplyDelete