tags:

views:

167

answers:

3

I have an object that has X number of fields. Each row in my JTable corresponds to one item. Each column in the row represents a field for that item. I also need to maintain a reference to the item (the item has a unique ID field as well) so I can determine the item in the selected cell.

Would the preferred approach to this be putting the actual object in each cell in the table and using various renderers to display the item or to simply put the field values in each of the cells and have a hidden column that has the item ID that I can reference when I need to know the item ID?

thanks, Jeff

+2  A: 

I'd write a custom table model (as opposed to trying to shoehorn your design into the default table model implementation). This model then holds your actual objects, and in its method implementations (such as getValueAt), it'd consider each of the fields as a column.


Simple example (not tested, but should illustrate the idea nicely):

import java.util.List;
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;

/**
 * Simple table model for displaying properties common to all objects.
 * Note that all methods must run inside the event dispatch thread.
 */
public class ObjectPropertyTableModel extends AbstractTableModel {
    private final List<Object> objects = new ArrayList<Object>();

    public void addObject(Object obj) {
        addObject(obj, objects.size());
    }

    public void addObject(Object obj, int index) {
        objects.add(index, obj);
        fireTableRowsInserted(index, index);
    }

    public void removeObject(Object obj) {
        int index = objects.indexOf(obj);
        objects.remove(index);
        fireTableRowsDeleted(index, index);
    }

    public Object getObject(int rowIndex) {
        return objects.get(rowIndex);
    }

    @Override
    public int getRowCount() {
        return objects.size();
    }

    @Override
    public int getColumnCount() {
        return 3;
    }

    @Override
    public String getColumnName(int columnIndex) {
        switch (columnIndex) {
        case 0: return "toString";
        case 1: return "hashCode";
        case 2: return "class";
        default: throw new IndexOutOfBoundsException();
        }
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        switch (columnIndex) {
        case 0: return String.class;
        case 1: return Integer.class;
        case 2: return Class.class;
        default: throw new IndexOutOfBoundsException();
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Object obj = objects.get(rowIndex);
        switch (columnIndex) {
        case 0: return obj.toString();
        case 1: return obj.hashCode();
        case 2: return obj.getClass();
        default: throw new IndexOutOfBoundsException();
        }
    }
}
Chris Jester-Young
That would mean that each cell in a row would have a reference to the same object, correct?
Jeff Storey
Not quite. The cells are "virtual" in the case of a custom table model. When the table wants to display a cell, it calls your `getValueAt` method. At that point, access your object's relevant field.
Chris Jester-Young
(Yes. Do work in the model. Keep renderers simple.)
Tom Hawtin - tackline
Yes, my fault. I was thinking if I extended DefaultTableModel, not implementing a TableModel. Oops...thanks.
Jeff Storey
Note that this solution does not provide means for retrieving the reference to a object on a row. Simple method should be added for retrieving it.
Carlos
A: 

My preferred approach is to have a table model which returns a value from the field for each column. To be able to get a reference to the object, I add a method to the model which could be called getObjectFromRow and as its name suggests, it returns the object corresponding to the row index. This way you can have the actual displayed data in the cell and you don't need a hidden column to get a reference to the row object. Of course this requires a custom table model but I think it's well worth the trouble.
Of course using renderers for displaying data from the correct field is also possible, but in my experience this has several problems, including the trouble of writing all the renderers for different data types and also the fact that copy & paste from the table does not work properly out of the box.

Carlos
+1  A: 

You might be able to use the Bean Table Model. If not, then the JButtonTableModel examples shows how you might implement the getValueAt/setValueAt() methods and take advantage of the functionality provided by the RowTableModel so you don't need to create the TableModel from scratch.

camickr
Thanks. Unfortunately they are not necessarily getXXX methods. They are getProperty(XXX) since the properties are determined at runtime, but this is a good one to know about.
Jeff Storey
Which is why I suggested the JButtonTableModel example might be better since it is easily customizable by overriding a couple of methods and you still have all the functionality you need. It supports methods for retrieving the row Object so you can get the ID even though its not visble in the table. It also supports dynamic add/remove of rows.
camickr