So I have been getting annoyed by the standard JTable and all of its less than intuitive functionality and prep-work.
What are my main complains with JTables?
1) If I add the data of an object into a JTable, the data in the JTable is largely disconnected from the object itself, which makes editing the Object by editing the JTable more difficult since it requires searching for objects and whatnot.
2) JTables are largely useless when not inside a JScrollPane.
3) Having to manage the JTable and the TableModel independently
4) The hellish cell rendering process when all I want is a checkbox
So, in an attempt to solve some of these problems for myself, I embarked on creating a Table object that would make my life significantly easier. Unfortunately, it contains a few classes, requires an implementation of an Interface, and is largely unfinished, but this is what I have so far:
Test Class:
/** * @(#)ObjectTableTesting.java * * ObjectTableTesting application * * @author * @version 1.00 2011/5/31 */ import javax.swing.*; public class ObjectTableTesting { public static void main(String[] args) { //Create JFrame And Set Size JFrame frame = new JFrame("Table Test"); frame.setSize(500,500); //Create Column Title Array String[] columnTitles = new String[]{"Value1","Value2","Value3","Value4","Value5","Value6"}; //Create CustomTable Object, where the width is 500, the height is 500, and I am sending my Title Array as the titles CustomTable table = new CustomTable(500,500,columnTitles); //Create 4 TestObjects TestObject obj0 = new TestObject("qwe","rty",0,1,0.1,1.2); TestObject obj1 = new TestObject("uio","pas",2,3,2.3,3.4); TestObject obj2 = new TestObject("dfg","hjk",4,5,4.5,5.6); TestObject obj3 = new TestObject("lzx","cvb",6,7,6.7,7.8); //Add Each TestObject to the table table.add(obj0); table.add(obj1); table.add(obj2); table.add(obj3); //Set the CustomTable to be the main content pane (for simplicity in this example) frame.setContentPane(table); //Finish work on JFrame frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
TestObject Class:
/** * @(#)TestObject.java * * * @author * @version 1.00 2011/5/31 */ public class TestObject implements TableObjectInterface { String value1; String value2; int value3; int value4; double value5; double value6; public TestObject(String v1,String v2,int v3,int v4,double v5,double v6) { value1 = v1; value2 = v2; value3 = v3; value4 = v4; value5 = v5; value6 = v6; } public Object getIndexValue(int i) { if(i==0) return value1; else if(i==1) return value2; else if(i==2) return value3; else if(i==3) return value4; else if(i==4) return value5; else return value6; } }
CustomTable Class:
/** * @(#)CustomTable.java * * * @author * @version 1.00 2011/5/3 */ import javax.swing.JTable; import javax.swing.JScrollPane; import java.awt.Dimension; import javax.swing.table.TableCellRenderer; import java.awt.Component; import javax.swing.JCheckBox; import javax.swing.DefaultCellEditor; import javax.swing.event.TableModelListener; import javax.swing.event.ListSelectionListener; import javax.swing.JViewport; import java.awt.Rectangle; import java.awt.Point; public class CustomTable extends JScrollPane { //The table object which is inside the JScrollPane JTable table; //The model for our table ObjectTableModel model; //The array of column names String[] columnNames; //The constructor public CustomTable(int width,int height,String[] cn) { //Call the JScrollPane constructor super(); //Set column names columnNames = cn; //Create model and table model = new ObjectTableModel(columnNames); table = new JTable(model); //Set the viewport and the preferred size of the JScrollPane setViewportView(table); setPreferredSize(new Dimension(width,height)); } //The simplest add method public void add(TableObjectInterface e) { model.add(e,0); } //A more complex add method for when creating several tables from the same objects, but containing different data public void add(TableObjectInterface e,int i) { model.add(e,i); } //Method to set the selected row in the table based on the array of Objects sent (representing the data in the row we are setting) public void setSelection(Object[] rowArray) { int row = model.getObjectRow(rowArray); if(row==-1) return; table.setRowSelectionInterval(row,row); } //Method to clear all data from the table public void clearTable() { model.removeAll(); } //Method that returns the Object associated to the selected row public TableObjectInterface getSelectedObject() { return model.getObject(table.getSelectedRow()); } //Method that removes and returns the Object associated to the selected row public TableObjectInterface removeSelectedObject() { if(table.getSelectedRow()==-1) return null; return model.removeObject(table.getSelectedRow()); } //Method to set a particular column's cell rendering to a checkbox public void setCheckBoxes(int col) { table.getColumnModel().getColumn(col).setCellRenderer(new TableCellRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,boolean isFocused, int row, int col) { JCheckBox rendererComponent = new JCheckBox(); rendererComponent.setSelected((Boolean)(value)); return rendererComponent; } }); DefaultCellEditor cellEditor = new DefaultCellEditor(new JCheckBox()); cellEditor.setClickCountToStart(1); table.getColumnModel().getColumn(col).setCellEditor(cellEditor); } //Method to add a TableModelListener to the model public void addTableListener(TableModelListener t) { model.addTableModelListener(t); } //Method to add a ListSelectionListener to the table public void addSelectionListener(ListSelectionListener t) { table.getSelectionModel().addListSelectionListener(t); } //Method to set the JScrollPane's viewport to the selected row public void scrollSelectedToRow() { if (!(table.getParent() instanceof JViewport)) { return; } int rowIndex = table.getSelectedRow(); if(rowIndex==-1) return; JViewport viewport = (JViewport)table.getParent(); // This rectangle is relative to the table where the // northwest corner of cell (0,0) is always (0,0). Rectangle rect = table.getCellRect(rowIndex, 0, true); // The location of the viewport relative to the table Point pt = viewport.getViewPosition(); // Translate the cell location so that it is relative // to the view, assuming the northwest corner of the // view is (0,0) rect.setLocation(rect.x-pt.x, rect.y-pt.y); // Scroll the area into view viewport.scrollRectToVisible(rect); } }
ObjectTableModel Class:
/** * @(#)ObjectTableModel.java * * * @author * @version 1.00 2011/5/3 */ import javax.swing.table.DefaultTableModel; import java.util.Vector; public class ObjectTableModel extends DefaultTableModel { //Vector of the items in the table, in order Vector<TableObjectInterface> items; //The column names of the table Object[] columnNames; //Constructor public ObjectTableModel(Object[] columnName) { //Call DefaultTableModel constructor super(columnName,0); //Set column names columnNames = columnName; //Initialize the items Vector items = new Vector<TableObjectInterface>(); } //Method to add an object to the table. t represents the offset index for when calling the object's getIndexValue method. t usually is 0. public void add(TableObjectInterface item,int t) { //Add the object to the items vector items.add(item); //Vector of data that will be added to the table Vector tempAdd = new Vector(); for(int i=0;i<columnNames.length;i++) { //Adding the values of the data to the tempAdd Vector tempAdd.add(item.getIndexValue(i+t)); } //Call the DefaultTableModel's addRow method addRow(tempAdd); } //Method to return the row of the object associated to the sent Object array public int getObjectRow(Object[] itemArray) { ROWLOOP: for(int i=0;i<getRowCount();i++) { for(int x=0;x<itemArray.length;x++) { if(!itemArray[x].equals(getValueAt(i,x))) continue ROWLOOP; } return i; } return -1; } //Method that removes all the data in the table public void removeAll() { while(getRowCount()>0) removeRow(0); items = new Vector<TableObjectInterface>(); } //Method that returns the object assoicated to the sent row public TableObjectInterface getObject(int row) { if(row==-1) return null; return items.get(row); } //Method that removes and returns the object associated to the sent row public TableObjectInterface removeObject(int row) { TableObjectInterface ret = items.remove(row); removeRow(row); return ret; } }
TableObjectInterface Class:
/** * @(#)TableObjectInterface.java * * * @author * @version 1.00 2011/5/3 */ public interface TableObjectInterface { //Method that determines what values will be put in what column in the table, where: i represents the column of the table public Object getIndexValue(int i); }
I would appreciate some feedback, suggestions, and improvement ideas.
Cheers