Reputation: 111
I have a JTable with checkbox as one of the column. I also have a checkbox in header to check/uncheck all. AFAIK the default behaviour of JTable is it deselects all the rows selected before if select a new row. But can we achieve CTRL click like behavior with checkbox. That retaining the previously selected row. The main problem I am facing is while enabling multiple selection of JTable rows using checkbox.
1st row is checked then first row is selected and if third row is checked then third is selected along with first row (which is already checked and selected)
When first row is checked and selected and if select the third row then it deselects all the rows previously selected and the third one is only selected.
I have a sample code which simulates the scenario I want to achieve same as the Add Another One button does, but with checkbox selection.
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
import javax.swing.event.CellEditorListener;
public class JTableRowSelectProgramatically extends JPanel {
final JTable table = new JTable(new MyTableModel());
public JTableRowSelectProgramatically() {
initializePanel();
}
private void initializePanel() {
setLayout(new BorderLayout());
setPreferredSize(new Dimension(475, 150));
table.setFillsViewportHeight(true);
JScrollPane pane = new JScrollPane(table);
JLabel label2 = new JLabel("Row: ");
final JTextField field2 = new JTextField(3);
JButton add = new JButton("Select");
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(false);
table.getSelectionModel().addListSelectionListener(new ListSelectionListenerImpl());
TableColumn tc = table.getColumnModel().getColumn(3);
tc.setCellEditor(table.getDefaultEditor(Boolean.class));
tc.setCellRenderer(table.getDefaultRenderer(Boolean.class));
((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true);
tc.getCellEditor().addCellEditorListener(new CellEditorListenerImpl());
add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
int index2 = 0;
try {
index2 = Integer.valueOf(field2.getText());
} catch (NumberFormatException e) {
e.printStackTrace();
}
table.addRowSelectionInterval(index2, index2);
field2.setText(String.valueOf(index2));
}
});
JPanel command = new JPanel(new FlowLayout());
command.add(label2);
command.add(field2);
command.add(add);
add(pane, BorderLayout.CENTER);
add(command, BorderLayout.SOUTH);
}
public static void showFrame() {
JPanel panel = new JTableRowSelectProgramatically();
panel.setOpaque(true);
JFrame frame = new JFrame("JTable Row Selection");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JTableRowSelectProgramatically.showFrame();
}
});
}
public class MyTableModel extends AbstractTableModel {
private String[] columns = {"ID", "NAME", "AGE", "A STUDENT?"};
private Object[][] data = {
{1, "Alice", 20, new Boolean(false)},
{2, "Bob", 10, new Boolean(false)},
{3, "Carol", 15, new Boolean(false)},
{4, "Mallory", 25, new Boolean(false)}
};
public int getRowCount() {
return data.length;
}
public int getColumnCount() {
return columns.length;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return data[rowIndex][columnIndex];
}
@Override
public String getColumnName(int column) {
return columns[column];
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 3;
}
//
// This method is used by the JTable to define the default
// renderer or editor for each cell. For example if you have
// a boolean data it will be rendered as a check box. A
// number value is right aligned.
//
@Override
public Class<?> getColumnClass(int columnIndex) {
return data[0][columnIndex].getClass();
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 3) {
data[rowIndex][columnIndex] = aValue;
fireTableCellUpdated(rowIndex, columnIndex);
}
}
}
class ListSelectionListenerImpl implements ListSelectionListener {
public void valueChanged(ListSelectionEvent lse) {
ListSelectionModel lsm = (ListSelectionModel) lse.getSource();
int row = table.getRowCount();
if (lsm.isSelectionEmpty()) {
} else {
// If any column is clicked other than checkbox then do normal selection
// i.e select the click row and deselects the previous selection
if (table.getSelectedColumn() != 3) {
for (int i = 0; i < row; i++) {
if (lsm.isSelectedIndex(i)) {
table.setValueAt(true, i, 3);
} else {
table.setValueAt(false, i, 3);
}
}
}
}
}
}
public class CellEditorListenerImpl implements CellEditorListener{
public void editingStopped(ChangeEvent e) {
for(int i=0; i<table.getRowCount();i++){
if((Boolean)table.getValueAt(i, 3)){
table.addRowSelectionInterval(i, i);
}
else{
table.removeRowSelectionInterval(i, i);
}
}
}
public void editingCanceled(ChangeEvent e) {
System.out.println("do nothing");
}
}
}
Upvotes: 6
Views: 14357
Reputation: 205775
Once you implement these TableModel
methods, you can use setValueAt()
in your button listeners to condition the model as required to keep the checkbox state and selection model in synchrony. There's a related example here.
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 3;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 3) {
data[rowIndex][columnIndex] = aValue;
fireTableCellUpdated(rowIndex, columnIndex);
}
}
Addendum: As a concrete example, your clear
listener might invoke a method in the TableModel
such as clearChecks()
:
MyTableModel model = (MyTableModel) table.getModel();
model.clearChecks();
...
private void clearChecks() {
for (int i = 0; i < data.length; i++) {
data[i][3] = false;
}
fireTableRowsUpdated(0, data.length);
}
Upvotes: 4
Reputation: 974
At a high level, I would invoke a key listener and check if VK_CTRL is flagged when inside your selection listener. This sounds like a logic block issue or a listener conflict.
Barring that, a JList box allows for multiple selections.
Upvotes: 0