Over a million developers have joined DZone.
Platinum Partner

Table Row Editing in Apache Pivot

· Java Zone

The Java Zone is brought to you in partnership with ZeroTurnaround. Discover how you can skip the build and redeploy process by using JRebel by ZeroTurnaround.

Conventional wisdom holds that there are two ways to edit table data.  You can either use an inline cell editor, or you can launch an edit dialog that lets you edit the whole record at a time.  Wouldn't it be neat if we could get the best of both worlds and edit the entire record inline?  As it turns out, Apache Pivot provides this type of table row editor out of the box.  What follows is a simple example of how to use this useful feature.

The following image shows a screen shot of the demo application that we'll be covering. A live example is available here. The application contains a small set of sample data that may be edited by double clicking on a row.

Sample Row Editing Application

The WTKX source for the demo is as follows:

<Window title="Row Editor Demo"
<Border styles="{padding:0}">
<ScrollPane horizontalScrollBarPolicy="fillToCapacity">
<TableView wtkx:id="tableView" selectMode="single">
<TableView.Column name="date" width="87" headerData="Date"/>
<TableView.Column name="type" width="125" headerData="Type"/>
<TableView.Column name="amount" width="75" headerData="Amount">
<content:TableViewNumberCellRenderer numberFormat="¤#,##0.00"/>
<TableView.Column name="description" width="1*" headerData="Description"/>
<demo:CustomTableRow date="2009-03-28" type="Travel"
amount="1286.90" description="Ticket #145-XX-71903-09"/>
<demo:CustomTableRow date="2009-03-28" type="Meals"
amount="34.12" description="Took client out"/>
<demo:CustomTableRow date="2009-03-31" type="Meals"
amount="27.00" description=""/>
<demo:CustomTableRow date="2009-04-01" type="Meals"
amount="12.55" description=""/>
<demo:CustomTableRow date="2009-04-02" type="Meals"
amount="18.86" description=""/>
<demo:CustomTableRow date="2009-04-02" type="Parking"
amount="30.00" description="Cambridge Center parking"/>
<demo:CustomTableRow date="2009-04-03" type="Meals"
amount="20.72" description=""/>
<demo:CustomTableRow date="2009-04-06" type="Hotel"
amount="529.90" description="Marriott reservation #DF-9982-BRN"/>
<TableViewHeader tableView="$tableView" styles="{headersPressable:false}"/>

It contains a maximized, decorationless window with a Border as its content. The Border contains a ScrollPane containing the data.  For the purposes of the demo, I hard-coded sample data into the WTKX. There are four columns: date, expense type, amount, and description. For amount, I use a cell renderer that formats the number as currency.

The Main Application Class

Since Pivot includes a default table view row editor, there's really not much required to get this demo up and running. However, the default row editor will use TextInput instances for the individual cell editors unless we tell it otherwise. In this case, I wanted to make a few changes. For date, I wanted use a Spinner with a custom data model. For expense type, I wanted a ListButton that would let the user choose among the different enum values for expense type. Finally, for amount, I wanted a TextInput that would validate the text such that only valid currency values would be accepted.

The startup() method of the application is where most of the work lies; it is shown below:

public void startup(Display display, Dictionary<String, String> properties)
throws Exception {
WTKXSerializer wtkxSerializer = new WTKXSerializer();
window = (Window)wtkxSerializer.readObject(getClass().getResource("demo.wtkx"));

TableView tableView = (TableView)wtkxSerializer.getObjectByName("tableView");
TableViewRowEditor tableViewRowEditor = new TableViewRowEditor();

// Date uses a Spinner with a CalendarDateSpinnerData model
Spinner dateSpinner = new Spinner(new CalendarDateSpinnerData());
tableViewRowEditor.getCellEditors().put("date", dateSpinner);

// Expense type uses a ListButton that presents the expense types
ListButton typeListButton = new ListButton
(new EnumList<ExpenseType>(ExpenseType.class));
tableViewRowEditor.getCellEditors().put("type", typeListButton);

// Amount uses a TextInput with strict currency validation
TextInput amountTextInput = new TextInput();
amountTextInput.setValidator(new CurrencyValidator());
amountTextInput.getStyles().put("strictValidation", true);
tableViewRowEditor.getCellEditors().put("amount", amountTextInput);

It first instantiates a WTKX serializer to load the UI from the WTKX file. It then obtains a reference to the table view and sets the row editor on the table view. Finally, it makes the desired modifications to the row editor by creating three custom cell editor components and mapping them to their corresponding columns using the row editor's getCellEditors() method. Note that the row editor uses Pivot's data binding facilities to get the data from the row into the cell editor components and to get the data back out when it's time to save the user's edits. As such, notice how I set up the data binding keys on each custom cell editor that I created.

Supporting Classes

The final pieces of the puzzle are the supporting classes that I've used in the demo: CurrencyValidator, CustomTableRow, and ExpenseType. First, let's look at the validator:

public class CurrencyValidator implements Validator {
public boolean isValid(String text) {
boolean valid = true;

if (text.length() > 0) {
try {
BigDecimal numericAmount = new BigDecimal(text);
valid = (numericAmount.scale() <= 2
&& numericAmount.signum() >= 0);
} catch (NumberFormatException ex) {
valid = false;

return valid;

This validator simply ensures that the text in the amount text input can be represented as a positive BigDecimal with a decimal precision less than or equal to 2.

The source for CustomTableRow is as follows:

public class CustomTableRow {
private CalendarDate calendarDate = null;
private ExpenseType type = null;
private double amount = 0;
private String description = null;

public CalendarDate getDate() {
return calendarDate;

public void setDate(CalendarDate calendarDate) {
this.calendarDate = calendarDate;

public final void setDate(String calendarDate) {
setDate(new CalendarDate(calendarDate));

public ExpenseType getType() {
return type;

public void setType(ExpenseType type) {
this.type = type;

public final void setType(String type) {

public double getAmount() {
return amount;

public void setAmount(double amount) {
this.amount = amount;

public final void setAmount(String amount) {
if (amount == null || amount.length() == 0) {
} else {

public String getDescription() {
return description;

public void setDescription(String description) {
this.description = description;

This is a simple value object with a few overloads. The first overload, setDate(String), is to support the creation of a CustomTableRow in WTKX. The second, setAmount(String), is to support binding a table row's amount to a TextInput.

Finally, let's take a look at the ExpenseType enum:

public enum ExpenseType {

For the sake of simplicity, I used human-readable values within the enum and presented the values using an EnumList set as the model of my ListButton. If this were a real-world application, I would have to consider localization and would have probably used a custom ListView.ItemRenderer on the list button.


So, building a table view row editor that allows the user to edit an entire record inline is very easy using Pivot, and it's a convenient new UI metaphor for editing table data. Note that Pivot's TableView.RowEditor interface does not preclude the application developer from building a more traditional editor (in fact, Pivot includes a stock TableViewCellEditor as well), leaving your options open as to which paradigm makes the most sense for you.

The full source for this demo is available here. For more information on Apache Pivot, visit http://incubator.apache.org/pivot.


The Java Zone is brought to you in partnership with ZeroTurnaround. Discover how you can skip the build and redeploy process by using JRebel by ZeroTurnaround.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}