I have a JXTable
compound of 6 columns and two of them are JCheckBox
. I would like to have the following behavior:
I edited an image with Photoshop to show the desired result:
For the CheckOne
and CheckTwo
columns i use a custom TableCellEditor
and TableCellRenderer
:
public class CheckBoxCellEditor extends AbstractCellEditor implements TableCellEditor {
private static final long serialVersionUID = 1L;
private JCheckBox checkBox = new JCheckBox();
public CheckBoxCellEditor() {
checkBox.setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
checkBox.setSelected(value==null ? false : (boolean)value);
return checkBox;
}
@Override
public Object getCellEditorValue() {
return checkBox.isSelected();
}
}
public class CheckBoxCellRenderer extends JCheckBox implements TableCellRenderer{
private static final long serialVersionUID = 1L;
public CheckBoxCellRenderer() {
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
setSelected(value==null ? false : (boolean)value);
return this;
}
}
And this is how i set them:
//CheckOne
table.getColumn(4).setCellEditor(new CheckBoxCellEditor());
table.getColumn(4).setCellRenderer(new CheckBoxCellRenderer());
//CheckTwo
table.getColumn(5).setCellEditor(new CheckBoxCellEditor());
table.getColumn(5).setCellRenderer(new CheckBoxCellRenderer());
I tried to add a PropertyChangeListener
to the JXTable
and implement the behavior from there, but i couldn´t get it done.
Then i realised that my custom editor and renderer were changing all the column components at the same time instead of only the desired component.
So, i tried to make the changes in the TableCellEditor
and TableCellRenderer
, and in the PropertyChangeListener
but, again, i couldn´t figure it out.
First off please note your problem is not related to JXTable but renderers / editors / model.
As @mKorbel points out JCheckBox is the default Renderer/Editor for booleans and generally speaking you won't need to re-invent the wheel: just override getColumnClass() properly to return Boolean on both 5th and 6th columns.
However, because of this requirement:
If the first checkbox is unchecked, the second must be disabled and unchecked.
This is not the default renderer's behavior so you actually need your own renderer. But only renderer, you don't need an editor: as @mKorbel points out you need a little work with the table model.
class CheckBoxCellRenderer implements TableCellRenderer {
private final JCheckBox renderer;
public CheckBoxCellRenderer() {
renderer = new JCheckBox();
renderer.setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Color bg = isSelected
? table.getSelectionBackground() : table.getBackground();
renderer.setBackground(bg);
renderer.setEnabled(table.isCellEditable(row, column));
renderer.setSelected(value != null && (Boolean)value);
return renderer;
}
}
You need to work on both isCellEditable() and setValueAt() methods to properly update your second booleans column based on values on first one. For instance consider this snippet:
// A default model with 5 rows and 6 columns
DefaultTableModel model = new DefaultTableModel(5, 6) {
@Override
public Class<?> getColumnClass(int columnIndex) {
switch(columnIndex) {
case 4:
case 5: return Boolean.class; // Both 5th and 6th columns are booleans
}
return super.getColumnClass(columnIndex);
}
@Override
public boolean isCellEditable(int row, int column) {
/*
* In order to know if 6th column is editable, you have to check
* 5th column's value.
*/
if(column == 5) {
Object value = getValueAt(row, 4);
return value != null && (Boolean)value;
}
return super.isCellEditable(row, column);
}
@Override
public void setValueAt(Object aValue, int row, int column) {
/*
* If 5th column value is updated to FALSE, you need to
* set 6th column's value to FALSE as well
*/
if(column == 4) {
super.setValueAt(Boolean.FALSE, row, 5);
}
super.setValueAt(aValue, row, column);
}
};
Here is a complete test case. Hope it helps.
import java.awt.Color;
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
/**
* @author dic19
*/
public class Demo {
private void createAndShowGui() {
DefaultTableModel model = new DefaultTableModel(5, 6) {
@Override
public Class<?> getColumnClass(int columnIndex) {
switch(columnIndex) {
case 4:
case 5: return Boolean.class;
}
return super.getColumnClass(columnIndex);
}
@Override
public boolean isCellEditable(int row, int column) {
if(column == 5) {
Object value = getValueAt(row, 4);
return value != null && (Boolean)value;
}
return super.isCellEditable(row, column);
}
@Override
public void setValueAt(Object aValue, int row, int column) {
if(column == 4) {
super.setValueAt(false, row, 5);
}
super.setValueAt(aValue, row, column);
}
};
JTable table = new JTable(model);
table.getDefaultRenderer(null);
table.getColumnModel().getColumn(5).setCellRenderer(new CheckBoxCellRenderer());
JScrollPane scrollPane = new JScrollPane(table);
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
class CheckBoxCellRenderer implements TableCellRenderer {
private final JCheckBox renderer;
public CheckBoxCellRenderer() {
renderer = new JCheckBox();
renderer.setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Color bg = isSelected
? table.getSelectionBackground() : table.getBackground();
renderer.setBackground(bg);
renderer.setEnabled(table.isCellEditable(row, column));
renderer.setSelected(value != null && (Boolean)value);
return renderer;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Demo().createAndShowGui();
}
});
}
}