Problem description:
originally there were two tables:
At the top of the left table there are two elements: JTextField and JComboBox, which are put into one separate JPanel. These two elementes are needed for the table filtering purposes, that is, changing selected combo box element or typing something in the JTextField get table on the left filtered out.
I tried to implement TableColumnModelListener interface in order to connect JTextField and JComboBox elements to the table. The result was not so good as I expected: the revalidate() method called uppon the JPanel container object ruined whole swing elements composition. The outcome of this experiment is showed in the following picture:
Source code:
TablesTest class:
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class TablesTest {
private JFrame frame = new JFrame();
private javax.swing.JPanel commonTablesPanel;
private javax.swing.JPanel dataTable1Panel;
private javax.swing.JPanel dataTable2Panel;
private javax.swing.JTable datatable1Table;
private javax.swing.JTable datatable2Table;
private javax.swing.JComboBox filterComboBox;
private javax.swing.JPanel filterPanel;
private javax.swing.JTextField filterTextField;
private javax.swing.JScrollPane jScrollPane3;
private javax.swing.JScrollPane jScrollPane4;
private TableResizeListener tableResizeListener1;
public TablesTest() {
java.awt.GridBagConstraints gridBagConstraints;
commonTablesPanel = new javax.swing.JPanel();
dataTable1Panel = new javax.swing.JPanel();
filterPanel = new javax.swing.JPanel();
filterTextField = new javax.swing.JTextField();
filterComboBox = new javax.swing.JComboBox();
jScrollPane3 = new javax.swing.JScrollPane();
datatable1Table = new javax.swing.JTable();
dataTable2Panel = new javax.swing.JPanel();
jScrollPane4 = new javax.swing.JScrollPane();
datatable2Table = new javax.swing.JTable();
frame.setLayout(new java.awt.GridBagLayout());
commonTablesPanel.setMinimumSize(new java.awt.Dimension(30, 30));
commonTablesPanel.setPreferredSize(new java.awt.Dimension(30, 30));
commonTablesPanel.setLayout(new java.awt.GridBagLayout());
dataTable1Panel.setLayout(new java.awt.GridBagLayout());
filterPanel.setLayout(new java.awt.GridBagLayout());
filterTextField.setPreferredSize(new java.awt.Dimension(30, 20));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
filterPanel.add(filterTextField, gridBagConstraints);
filterComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
filterComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
//TODO
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
filterPanel.add(filterComboBox, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
dataTable1Panel.add(filterPanel, gridBagConstraints);
jScrollPane3.setPreferredSize(new java.awt.Dimension(30, 30));
datatable1Table.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null}
},
new String [] {
"Title 1", "Titile 2"
}
));
datatable1Table.setMinimumSize(new java.awt.Dimension(30, 30));
jScrollPane3.setViewportView(datatable1Table);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 0.1;
gridBagConstraints.weighty = 0.1;
dataTable1Panel.add(jScrollPane3, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 0.1;
gridBagConstraints.weighty = 0.1;
commonTablesPanel.add(dataTable1Panel, gridBagConstraints);
dataTable2Panel.setLayout(new java.awt.GridBagLayout());
jScrollPane4.setPreferredSize(new java.awt.Dimension(30, 30));
datatable2Table.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null},
{null, null}
},
new String [] {
"Titile 1", "Title 2"
}
) {
boolean[] canEdit = new boolean [] {
false, false
};
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
});
datatable2Table.setMinimumSize(new java.awt.Dimension(30, 30));
jScrollPane4.setViewportView(datatable2Table);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 0.1;
gridBagConstraints.weighty = 0.1;
dataTable2Panel.add(jScrollPane4, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 0.1;
gridBagConstraints.weighty = 0.1;
commonTablesPanel.add(dataTable2Panel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 0.1;
gridBagConstraints.weighty = 0.1;
gridBagConstraints.insets = new java.awt.Insets(4, 4, 0, 4);
frame.add(commonTablesPanel, gridBagConstraints);
tableResizeListener1 = new TableResizeListener(datatable1Table, filterPanel);
frame.setPreferredSize(new Dimension(400,150));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TablesTest tableTest = new TablesTest();
}
});
}
}
Notice
tableResizeListener1 = new TableResizeListener(datatable1Table, filterPanel);
part. This is where all table columns get connected to the corresponding JPanel elements (JTextField and JComboBox in this example). You can comment this line out if you want to observe how does the composition look like without connected elements.
TableResizeListener class. This is where the problem lurks.
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.TableColumnModel;
public class TableResizeListener implements TableColumnModelListener {
private static final long serialVersionUID = 1L;
private JTable table = null;
private JPanel filterRow = null;
public TableResizeListener(JTable table, JPanel filterRow) {
this.table = table;
this.filterRow = filterRow;
this.table.getColumnModel().addColumnModelListener(this);
columnMarginChanged(new ChangeEvent(table.getColumnModel()));
}
// Implement TableColumnModelListener methods
// (Note: instead of implementing a listener you should be able to
// override the columnMarginChanged and columMoved methods of JTable)
@Override
public void columnMarginChanged(ChangeEvent e) {
TableColumnModel tcm = table.getColumnModel();
int columns = tcm.getColumnCount();
if(columns == filterRow.getComponentCount()) {
for (int i = 0; i < columns; i++) {
Component comp = filterRow.getComponent(i);
Dimension d = comp.getPreferredSize();
d.width = tcm.getColumn(i).getWidth();
comp.setPreferredSize(d);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
filterRow.revalidate();
}
});
}
}
@Override
public void columnMoved(TableColumnModelEvent e) {
Component moved = filterRow.getComponent(e.getFromIndex());
filterRow.remove(e.getFromIndex());
filterRow.add(moved, e.getToIndex());
filterRow.validate();
}
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
}
The line
filterRow.revalidate();
is responsible for this bug. I tried to call repaint(), validate(), however it resulted in not such a smooth visual experience.
How can I connect elements to the outside of the JTable without ruining my visual composition?