Java is one of the greatest object oriented languages. This can easily be seen when using swing components. JTables are a great example. Briefly, jTables are just a base to display a matrix of JComponents. So inside a JTable you can embed any object which extends the JComponent class.
Today I’ll show you an easy way to display a JTable inside another jTable as a cellRenderer. If you’ve ever worked with multivalued data models this can be of great help. In this simple example I’ll create a small contact manager where the name, phones, and e-mail addresses of the contact can be stored. If the contact has more than one e-mail address or phone number, then they’ll be displayed using an embedded jTable.
1 2 3 4 5 6 7 |
/* We initialize the Objects where we'll store our data */ /* First an array Object which will be our main table */ Object[][] data = null; /* We create an Array String to hold the name of the main table columns In this example case we are going to store a contact name, his email address(es) and his phone numer(s) We're also going to store the creation date.*/ String[] columns = {"Name", "E-Mail","Phone","Creation Date"}; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
/* First thing is to populate our data object with some example values*/ public void populateData(){ /* Two contacts*/ data = new Object[3][columns.length]; data[0][0] = "Peter"; String[] emails = {"peter@yahoo.com", "strange@name.com"}; data[0][1] = emails; String[] phones = {"555 35 25 65" , "555 35 24 63"}; data[0][2] = phones; data[0][3] = new Date(); data[1][0] = "Jackson"; data[1][1] = "Jack@hotmail.com"; String[] phones2 = {"555 35 24 33" , "555 11 88 88", "332 55 25 34"}; data[1][2] = phones2; data[1][3] = new Date(); data[2][0] = "Robert"; data[2][1] = "rob@hotmail.com"; data[2][2] = "555 28 95 81"; data[2][3] = new Date(); /* As you can see, we've stored two contacts one with two e-mail accounts and two phone numbers, and another member with three phone numbers*/ } /* Next we create our table models */ public void createModel(){ /* First we create the main model We overide the AbstractTableModel necessary methods*/ AbstractTableModel modelo = new AbstractTableModel() { public String getColumnName(int col) { return columns[col]; } public Class getColumnClass(int col) { if(getRowCount() <1) { return null; } return data[0][col].getClass(); } public int getRowCount() { return data.length; } public int getColumnCount() { return columns.length; } public Object getValueAt(int row, int col) { return data[row][col]; } public boolean isCellEditable(int row, int col){ return true; } public void setValueAt(Object value, int row, int col) { data[row][col] = value; fireTableCellUpdated(row, col); } }; /* We apply the model to the main jTable */ jTableData.setModel(modelo); /* We create a cell Renderer to display the data of the multivalue fields*/ TableCellRenderer jTableCellRenderer = new TableCellRenderer() { /* These are necessary variables to store the row's height */ private int minHeight = -1; private int currHeight = -1; /* Magic Happens */ public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { /* If what we're displaying isn't an array of values we return the normal renderer*/ if(!value.getClass().isArray()){ return table.getDefaultRenderer(value.getClass()) .getTableCellRendererComponent(table, value, isSelected, hasFocus,row, column); } else{ final Object[] passed = (Object[])value; /* We calculate the row's height to display data * This is not complete and has some bugs that * will be analyzed in further articles */ if(minHeight == -1){ minHeight = table.getRowHeight(); } if(currHeight != passed.length*minHeight){ currHeight = passed.length * minHeight; table.setRowHeight(row,currHeight); } /* We create the table that will hold the multivalue * fields and that will be embedded in the main table */ return new JTable(new AbstractTableModel() { public int getColumnCount() { return 1; } public int getRowCount() { return passed.length; } public Object getValueAt(int rowIndex, int columnIndex) { return passed[rowIndex]; } public boolean isCellEditable(int row, int col){ return true; } }); } } }; /* Finally we apply the new cellRenderer to the whole table */ TableColumnModel tcm = jTableData.getColumnModel(); for(int it = 0; it < tcm.getColumnCount(); it++){ tcm.getColumn(it).setCellRenderer(jTableCellRenderer); } // Note: if we need to edit the values inside the embedded jtable // we will need to create a TableCellEditor too. } |
In the above image you can see the result. The code will only allow you to display the data, but you won’t be able to edit. If you want to edit the data you must also create a table cell editor. I’ll analyze this in further posts.
Hello — any chance you’ll be updating this any time soon? I’m using the code, and it works, but I’d like to be able to find out which row was clicked on the inner (embedded) table.
Also, I sent you email, and it bounced with a “Message could not be delivered to the domain – marcnuri.com.” message.
The problem with this is that what you click when clicking the embedded table is just the visible representation of your data. To be able to manage this data you’ll have to create your own TableCellEditor just as we created the TableCellRenderer. When I get some spare time maybe I’ll do a tutorial.
For the time being you can try adapting this code Creating a Custom Table Cell Editor in a JTable Component
About the E-Mail bouncing, maybe the server was down when you tried.
Hello,
thanks for your code, it was really useful to me but I have some problems when I display the table, if I go to other window then when I return, the table and also the menu and other components that I have in the same window where is the table disappear, you know why its happening this??
Thanks a lot.
Hi Marc — thanks for the quick response and the link. I appreciate it!
please help me in creating cell editing for embbed jtable also. need your please
thanks
Hi Thanks for the post, it helped me go in the right direction. Just a note could you please format your code as it is very difficult to read.
Nice description.
I have question.Can we add Combox in Jtable header e.g. if we select any action in combo box then it affects whole table (e.g. table bacground color) ?
Hi, I have a problem, I copy this code and paste it in my java class (main class) but when I run it it only says “BUILD SUCCESSFUL (total time: 1 second)” maybe because of this
public static void main(String[] args) {
}
what should I do? Thanks!
Hi Marc,
I am trying to use it to make nested tables n levels deep but unable to select the cell present in inner table. Could you please help by suggesting the way forward?
Olá como faco pra mudar a cor de uma célula do jTable quando a coluna de status de um cliente for pago ou a receber
Uso o jtable do Netbeans
There was an issue on rhel4 where if a tracing preoscs didn’t call ptrace detach that the thread group leader would stay in traced state. If someone sent it a SIGCONT the thread group leader would continue and all the child threads would go into a traced state (T in ps). A second SIGCONT would bring it back to normal. Between the two signals a ps ax would show only the thread group leader status. Be sure to check the threads in ps to make sure none of them are in a traced state.
Hi Marc,
Thank you, the code is perfectly working for me.
If I try to copy the table data, the embedded cell values are copied as an Object
i.e. Peter [Ljava.lang.Object;@20785a37 [Ljava.lang.Object;@2d649736 04-abr-2007
How can i copy the data as it is for embedded cells instead of Objects.