I'm using NetBeans IDE to create a simple test of an issue I'm having with one of my projects (so please excuse some of the uncessary bloat from NetBeans, but it's not too bad really).
I have a JTable that has a cell editor with a JComboBox that's editable (so the user can select an item from the list or type new text in the combobox).
When the user tabs out of the combo box the combox box's cell editor loses the text typed into it.
How can I adjust this code so that when I tab away from the combo box after typing it will keep the text?
package testtablecellfocuslostissue;
import java.awt.Color;
import java.awt.Component;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
/**
*
* @author admin
*/
public class Main extends javax.swing.JFrame {
/**
* Creates new form Main
*/
public Main() {
initComponents();
table1.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
DefaultTableModel model = (DefaultTableModel) table1.getModel();
String[] values = new String[] { "Maintained By", "Endpoint" };
JComboBox comboBox = new JComboBox(values);
TableColumn col = table1.getColumnModel().getColumn(0);
col.setCellEditor(new MyComboBoxEditor(comboBox));
col.setCellRenderer(new MyComboBoxRenderer(values));
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
table1 = new javax.swing.JTable();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
table1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null}
},
new String [] {
"Key", "Value"
}
));
jScrollPane1.setViewportView(table1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 167, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Main().setVisible(true);
}
});
}
class MyComboBoxRenderer extends JComboBox implements TableCellRenderer {
public MyComboBoxRenderer(String[] items) {
super(items);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
//setForeground(table.getSelectionForeground());
setForeground(Color.BLACK);
super.setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelectedItem(value);
return this;
}
}
class MyComboBoxEditor extends DefaultCellEditor {
private JComboBox comboBox;
public MyComboBoxEditor(JComboBox comboBox) {
super(comboBox);
comboBox.setEditable(true);
}
}
// Variables declaration - do not modify
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable table1;
// End of variables declaration
}
This seems to have done the trick, but wow what a nightmare it was coming to this solution:
package testtablecellfocuslostissue;
import java.util.ArrayList;
import javax.swing.AbstractCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
/**
*
* @author admin
*/
public class Main extends javax.swing.JFrame {
/**
* Creates new form Main
*/
public Main() {
initComponents();
// Initial choices in the list
ArrayList<String> values = new ArrayList();
values.add("Male");
values.add("Female");
table1.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
TableColumn col = table1.getColumnModel().getColumn(0);
col.setCellEditor(new MyComboBoxEditor(new ArrayList(values)));
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
table1 = new javax.swing.JTable();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
table1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null}
},
new String [] {
"Key", "Value"
}
));
jScrollPane1.setViewportView(table1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 167, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
setLocationRelativeTo(null);
}// </editor-fold>
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Main().setVisible(true);
}
});
}
public class MyComboBoxEditor extends AbstractCellEditor implements TableCellEditor {
private JComboBox comboBox;
private ArrayList<String> comboBoxChoices;
public MyComboBoxEditor(ArrayList<String> choices) {
this.comboBoxChoices = choices;
}
@Override
public JComponent getTableCellEditorComponent(JTable table, Object item, boolean isSelected, int row, int column) {
comboBox = new JComboBox(comboBoxChoices.toArray());
comboBox.setEditable(true);
if (item != null) {
comboBox.setSelectedItem(item);
}
return comboBox;
}
@Override
public Object getCellEditorValue() {
return comboBox.getEditor().getItem();
}
@Override
public boolean stopCellEditing()
{
DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
Object editingValue = getCellEditorValue();
// Needed because your TableModel is empty
if (editingValue == null){
return super.stopCellEditing();
}
// Get the selected index
int selectedIndex = comboModel.getIndexOf(editingValue);
if (! (selectedIndex == -1)){
return super.stopCellEditing();
}
comboBoxChoices.add((String)editingValue);
return super.stopCellEditing();
}
}
// Variables declaration - do not modify
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable table1;
// End of variables declaration
}