It's actually not that hard to figure out but luckily I'm here to save you some time. So let's say we have a custom widget that we would like to use in a CellTable. In this example I will be using my StarRating widget from a previous post which you can find here: http://programmingfortherestofus.blogspot.com/2012/04/gwt-star-rating-widget-with-half-stars.html
First step we need to take is to create a class that extends AbstractCell and implements Cell so the CellTable knows how to draw the cell and what data type it will expect, as well as overrides the render method where we add our widget's HTML to an HtmlBuilder. And if we are allowing user interaction we first pass what events we want to capture to the super class, for example mouse click and the enter key press, and then we override the onBrowserEvent, onEnterKeyDown, and any other events we wanted to capture.
StarRatingCell.java
/** * */ package com.yourproject.widgets; import com.google.gwt.cell.client.AbstractCell; import com.google.gwt.cell.client.Cell; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; /** * @author Austin_Rappa * */ public class StarRatingCell extends AbstractCell<StarRating> implements Cell<StarRating> { /** * Cell that contains a StarRating widget */ public StarRatingCell() { super("click", "keydown"); } /** * Render a cell as HTML into a SafeHtmlBuilder, suitable for passing to Element.setInnerHTML(String) on a container element. */ @Override public void render(com.google.gwt.cell.client.Cell.Context context, StarRating value, SafeHtmlBuilder sb) { sb.appendHtmlConstant("<div style=\"padding:0px;\">"); if (value != null) { sb.appendHtmlConstant(value.toString()); } sb.appendHtmlConstant("</div>"); } /** * Called when an event occurs in a rendered instance of this Cell. The * parent element refers to the element that contains the rendered cell, NOT * to the outermost element that the Cell rendered. */ @Override public void onBrowserEvent(Context context, Element parent, StarRating value, NativeEvent event, ValueUpdater<StarRating> valueUpdater) { super.onBrowserEvent(context, parent, value, event, valueUpdater); // Handle the click event. if ("click".equals(event.getType())) { // Ignore clicks that occur outside of the outermost element. EventTarget eventTarget = event.getEventTarget(); if (parent.isOrHasChild(Element.as(eventTarget))) { // use this to get the selected element!! Element el = Element.as(eventTarget); //Get the value, if it is clear then set it to zero if (el.getAttribute("title").equals("clear")) { value.setRating(0); } else { value.setRating(Integer.parseInt(el.getAttribute("title"))); }
//Set the value of the StarRating setValue(context, parent, value); } } }; /** * onEnterKeyDown is called when the user presses the ENTER key will the * Cell is selected. You are not required to override this method, but its a * common convention that allows your cell to respond to key events. */ @Override protected void onEnterKeyDown(Context context, Element parent, StarRating value, NativeEvent event, ValueUpdater<StarRating> valueUpdater) { setValue(context, parent, value); } }
I mean how much easier can it get? Well I would like to introduce you to the next step, to create the column class that holds our StarRatingCell and overrides the getValue method to return a StarRating object.
StarRatingColumn.java
package com.yourproject.widgets;
import com.google.gwt.user.cellview.client.Column;
/**
* A column that displays its contents with a {@link StarRating} and does not make use of view data.
*
* @param <T> the row type
* @author Austin_Rappa
*/
public abstract class StarRatingColumn<T> extends Column<T, StarRating> {
/**
* Construct a new StarRatingColumn.
*/
public StarRatingColumn() {
super(new StarRatingCell());
}
/**
* Return the passed-in object.
* @param object The value to get
*/
@Override
public StarRating getValue(T object) {
return null;
}
}
And with this we are all ready to start puting this into a CellTable! We instantiate the StarRatingColumn and define the type that contains our rating value, then override the getValue method, and finally set a field updater when allowing user interaction to update the html in the table when the value changes.
CellTable<ThingIWantToRate> cellTable = new CellTable<ThingIWantToRate>();
...
StarRatingColumn<ThingIWantToRate> ratingColumn = new StarRatingColumn<ThingIWantToRate>() {
@Override
public StarRating getValue(ThingIWantToRate object) {
return new StarRating(object.getRating(), 10, false);
}
};
cellTable.addColumn(ratingColumn, "Rating");
//Update view
ratingColumn.setFieldUpdater(new FieldUpdater<ThingIWantToRate, StarRating>() {
public void update(int index, ThingIWantToRate object, StarRating value) {
//Set cell's value
object.setRating(value.getRating());
//TODO: Update database here
//Redraw table
cellTable.redraw();
}
});
This doesn't seem to work. The widget works great on it's own, but once inside a CellTable, the mouse events no longer seem to reach the widget. No mouseover, no click. Is there a way to pass these events from the the cell to the widget? I've tried using GWT 2.3 and 2.4.
ReplyDeleteThanks,
Robert
Good catch Robert. All we would have to do is tell our AbstractCell super class what browser events we would like to handle then add the methods to handle those browser events. I've updated the code above as well as a slight update to the StarRating in my previous post.
DeleteThanks!
This comment has been removed by the author.
ReplyDelete