Selección de varias filas en JTable
Tengo una JTable, que tiene una columna que es texto que no es editable y la segunda columna es una casilla de verificación que muestra valores booleanos... Ahora lo que quiero es, cuando el usuario selecciona varias filas y desmarca cualquiera de las casillas de verificación seleccionadas, todas las casillas de verificación bajo selección deben quedar desmarcadas y viceversa.
Utilizando el ejemplo de @Hovercraft y los consejos de @camickr, el siguiente ejemplo muestra una interfaz de usuario adecuada. Aunque utiliza botones, SelectionAction
también sería adecuado para un menú o ventana emergente.
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.DefaultListSelectionModel;
import javax.swing.table.DefaultTableModel;
/** @see http://stackoverflow.com/questions/4526779 */
public class CheckABunch extends JPanel {
private static final int CHECK_COL = 1;
private static final Object[][] DATA = {
{"One", Boolean.TRUE}, {"Two", Boolean.FALSE},
{"Three", Boolean.TRUE}, {"Four", Boolean.FALSE},
{"Five", Boolean.TRUE}, {"Six", Boolean.FALSE},
{"Seven", Boolean.TRUE}, {"Eight", Boolean.FALSE},
{"Nine", Boolean.TRUE}, {"Ten", Boolean.FALSE}};
private static final String[] COLUMNS = {"Number", "CheckBox"};
private DataModel dataModel = new DataModel(DATA, COLUMNS);
private JTable table = new JTable(dataModel);
private DefaultListSelectionModel selectionModel;
public CheckABunch() {
super(new BorderLayout());
this.add(new JScrollPane(table));
this.add(new ControlPanel(), BorderLayout.SOUTH);
table.setPreferredScrollableViewportSize(new Dimension(250, 175));
selectionModel = (DefaultListSelectionModel) table.getSelectionModel();
}
private class DataModel extends DefaultTableModel {
public DataModel(Object[][] data, Object[] columnNames) {
super(data, columnNames);
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == CHECK_COL) {
return getValueAt(0, CHECK_COL).getClass();
}
return super.getColumnClass(columnIndex);
}
@Override
public boolean isCellEditable(int row, int column) {
return column == CHECK_COL;
}
}
private class ControlPanel extends JPanel {
public ControlPanel() {
this.add(new JLabel("Selection:"));
this.add(new JButton(new SelectionAction("Clear", false)));
this.add(new JButton(new SelectionAction("Check", true)));
}
}
private class SelectionAction extends AbstractAction {
boolean value;
public SelectionAction(String name, boolean value) {
super(name);
this.value = value;
}
@Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < dataModel.getRowCount(); i++) {
if (selectionModel.isSelectedIndex(i)) {
dataModel.setValueAt(value, i, CHECK_COL);
}
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("CheckABunch");
frame.add(new CheckABunch());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowUI();
}
});
}
}
El problema es que cuando haces clic en una casilla de verificación para cambiar el valor de la misma, se perderá la selección de todas las filas. Por lo tanto, es posible que necesite hacer clic con el botón derecho del mouse para mostrar un menú emergente que contiene valores de selección/deseleccionación.
Luego puede usar table.getSelectedRows() para obtener los índices de todas las filas seleccionadas que necesita actualizar.
Puede obtener el intervalo de selección con un código similar a este:
table.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
public void valueChanged(ListSelectionEvent e) {
minSelectedRow = ((DefaultListSelectionModel)e.getSource()).getMinSelectionIndex();
maxSelectedRow = ((DefaultListSelectionModel)e.getSource()).getMaxSelectionIndex();
}
});
Luego, cuando se marca una casilla de verificación (escuche ItemEvent
), debe iterar desde a minSelectedRow
y maxSelectedRow
cambiar el estado de las casillas marcadas. Eso es todo.
Estoy de acuerdo con Roman en que su idea funcionaría si usara un campo de clase para mantener la selección mínima y máxima. Por ejemplo:
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class CheckABunch extends JPanel {
private static final Object[][] DATA = {{"One", Boolean.TRUE}, {"Two", Boolean.FALSE},
{"Three", Boolean.TRUE}, {"Four", Boolean.FALSE}, {"Five", Boolean.TRUE},
{"Six", Boolean.FALSE}, {"Seven", Boolean.TRUE}, {"Eight", Boolean.FALSE}};
private static final String[] COLUMNS = {"Number", "CheckBox"};
private DefaultTableModel model = new DefaultTableModel(DATA, COLUMNS) {
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 1) {
return getValueAt(0, 1).getClass();
}
return super.getColumnClass(columnIndex);
}
};
private JTable table = new JTable(model);
private int minSelectedRow = -1;
private int maxSelectedRow = -1;
boolean tableModelListenerIsChanging = false;
public CheckABunch() {
add(new JScrollPane(table));
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
minSelectedRow = ((DefaultListSelectionModel) e.getSource()).getMinSelectionIndex();
maxSelectedRow = ((DefaultListSelectionModel) e.getSource()).getMaxSelectionIndex();
}
});
model.addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
if (tableModelListenerIsChanging) {
return;
}
int firstRow = e.getFirstRow();
int column = e.getColumn();
if (column != 1 || maxSelectedRow == -1 || minSelectedRow == -1) {
return;
}
tableModelListenerIsChanging = true;
boolean value = ((Boolean)model.getValueAt(firstRow, column)).booleanValue();
for (int i = minSelectedRow; i <= maxSelectedRow; i++) {
model.setValueAt(Boolean.valueOf(value), i, column);
}
// *** edit: added two lines
minSelectedRow = -1;
maxSelectedRow = -1;
tableModelListenerIsChanging = false;
}
});
}
private static void createAndShowUI() {
JFrame frame = new JFrame("CheckABunch");
frame.getContentPane().add(new CheckABunch());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}