Fork us on GitHub

Understanding The Table Component

Table is sadly was not as documented as it should be
Post Image

Understanding The Table Component

Table is a composite component (but it isn’t a lead component), this means it is a subclass of Container. It’s effectively built from multiple components.

Table is heavily based on the TableLayout class. It’s important to be familiar with that layout manager when working with Table.

Here is a trivial sample of using the standard table component:

Form hi = new Form("Table", new BorderLayout());
TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] {
    {"Row 1", "Row A", "Row X"},
    {"Row 2", "Row B", "Row Y"},
    {"Row 3", "Row C", "Row Z"},
    {"Row 4", "Row D", "Row K"},
    }) {
        public boolean isCellEditable(int row, int col) {
            return col != 0;
        }
    };
Table table = new Table(model);
hi.add(BorderLayout.CENTER, table);
hi.show();
Simple Table usage
Figure 1. Simple Table usage
In the sample above the title area and first column aren’t editable. The other two columns are editable.

The more "interesting" aspects of the Table class can be achieved via the TableLayout. You can use the layout constraints (also exposed in the table class) to create spanning and elaborate UI’s.

E.g.:

Form hi = new Form("Table", new BorderLayout());
TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] {
    {"Row 1", "Row A", "Row X"},
    {"Row 2", "Row B can now stretch", null},
    {"Row 3", "Row C", "Row Z"},
    {"Row 4", "Row D", "Row K"},
    }) {
        public boolean isCellEditable(int row, int col) {
            return col != 0;
        }
    };
Table table = new Table(model) {
    @Override
    protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) {
        TableLayout.Constraint con =  super.createCellConstraint(value, row, column);
        if(row == 1 && column == 1) {
            con.setHorizontalSpan(2);
        }
        con.setWidthPercentage(33);
        return con;
    }
};
hi.add(BorderLayout.CENTER, table);
Table with spanning & fixed widths to 33%
Figure 2. Table with spanning & fixed widths to 33%

In order to customize the table cell behavior you can derive the Table to create a "renderer like" widget, however unlike the list this component is "kept" and used as is. This means you can bind listeners to this component and work with it as you would with any other component in Codename One.

So lets fix the example above to include far more capabilities:

Table table = new Table(model) {
    @Override
    protected Component createCell(Object value, int row, int column, boolean editable) { (1)
        Component cell;
        if(row == 1 && column == 1) {  (2)
            Picker p = new Picker();
            p.setType(Display.PICKER_TYPE_STRINGS);
            p.setStrings("Row B can now stretch", "This is a good value", "So Is This", "Better than text field");
            p.setSelectedString((String)value);  (3)
            p.setUIID("TableCell");
            p.addActionListener((e) -> getModel().setValueAt(row, column, p.getSelectedString()));  (4)
            cell = p;
        } else {
            cell = super.createCell(value, row, column, editable);
        }
        if(row > -1 && row % 2 == 0) {  (5)
            // pinstripe effect
            cell.getAllStyles().setBgColor(0xeeeeee);
            cell.getAllStyles().setBgTransparency(255);
        }
        return cell;
    }

    @Override
    protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) {
        TableLayout.Constraint con =  super.createCellConstraint(value, row, column);
        if(row == 1 && column == 1) {
            con.setHorizontalSpan(2);
        }
        con.setWidthPercentage(33);
        return con;
    }
};
1 The createCell method is invoked once per component but is similar conceptually to the List renderer. Notice that it doesn’t return a "rubber stamp" though, it returns a full component.
2 We only apply the picker to one cell for simplicities sake.
3 We need to set the value of the component manually, this is crucial since the Table doesn’t "see" this.
4 We need to track the event and update the model in this case as the Table isn’t aware of the data change.
5 We set the "pinstripe" effect by coloring even rows. Notice that unlike renderers we only need to apply the coloring once as the Components are stateful.
Table with customize cells using the pinstripe effect
Figure 3. Table with customize cells using the pinstripe effect
Picker table cell during edit
Figure 4. Picker table cell during edit
Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.