Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Understanding the Codename One Table Component

DZone's Guide to

Understanding the Codename One Table Component

Table is a subclass of Container, and a composite component. Here's a look at the Codename One table component. , with an example to explore this idea.

· Mobile Zone
Free Resource

Get gorgeous, multi-touch charts for your iOS application with just a few lines of code.

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.

Tip: 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

Note that in the sample above the title area and first column aren’t editable. The other two columns are editable!

The more "interesting" capabilities of the Table class can be utilized 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 and fixed widths

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 let's 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;
    }
};

Notice in the above code that:

  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 customized cells using the pinstripe effect
Picker table cell during editFigure 4. Picker table cell during edit

Final Word

We took a radically different approach from Swing with Tables. After following Swing with the List API, we came to the conclusion that the renderer approach is one of the hardest things for developers to pick up. 

If we would have taken that approach for Table we would have had to have an Editor API as well and that would have brought the complexity of this component to a completely different level.

.Net developers: use Highcharts, the industry's leading interactive charting library, without writing a single line of JavaScript.

Topics:
java ,codenameone ,android ,ios ,mobile

Published at DZone with permission of Shai Almog, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}