Multiple row selection in JTable

21,106

Solution 1

Using @Hovercraft's example and @camickr's advice, the example below shows a suitable user interface. Although it uses buttons, the SelectionAction would also be suitable for a menu or popup.

Check A Bunch

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();
            }
        });
    }
}

Solution 2

The problem is that when you click on a check box to change the value of the check box, the selection of all the rows will be lost. So you may need to use a right mouse click to display a popup menu that contains select/deselect values.

Then you can use table.getSelectedRows(), to get the indexes of all the selected rows you need to update.

Solution 3

You can get the selection interval with code similar to this:

table.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
    public void valueChanged(ListSelectionEvent e) {
        minSelectedRow = ((DefaultListSelectionModel)e.getSource()).getMinSelectionIndex();
        maxSelectedRow = ((DefaultListSelectionModel)e.getSource()).getMaxSelectionIndex();
    }
});

Then, when one checkbox is checked (listen to ItemEvent) you should iterate from the minSelectedRow to the maxSelectedRow and change checked boxes state. That's it.

Solution 4

I agree with Roman that his idea would work if you use a class field to hold the min and max selection. For instance:

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();
            }
        });
    }
}
Share:
21,106
sasidhar
Author by

sasidhar

I am a computer science engineering student.

Updated on July 05, 2022

Comments

  • sasidhar
    sasidhar almost 2 years

    I have a JTable, that has one column that is text which is not editable and the second column is a check box that displays boolean values.... Now what i want is, when the user selects multiple rows and unchecks any one of the selected check boxes, then all the check boxes under selection should get unchecked and vice versa.

  • sasidhar
    sasidhar over 13 years
    you are right.... when ever the check box is clicked all the row selection is lost..... is there any way around this without using the right click..?
  • sasidhar
    sasidhar over 13 years
    @Hovercraft Full Of Eels though your code makes sense, when i executed on my system, its behaving in a quite unexpected way, the bunch selection works just fine, but after that the normal selection is also firing bunch selection......
  • sasidhar
    sasidhar over 13 years
    @Hovercraft Full Of Eels check box 2, uncheck box 2 and then check box 4..... then automatically box 2 is being checked......
  • Hovercraft Full Of Eels
    Hovercraft Full Of Eels over 13 years
    I'm not surprised as that should occur if 2 is still selected, which was I thought the desired behavior for the program: If you check or uncheck a check box, all selected rows will mimic that check box's state, including a single selected row left over from the last selection. If you don't desire this behavior, one way around it is to set the min and max selected row to -1 at the end of the TableModelListener's tableChanged method. I'll update the code below to show you what I mean.
  • trashgod
    trashgod over 13 years
    +1 Good example. It works as described, but it only handles contiguous selections.
  • Hovercraft Full Of Eels
    Hovercraft Full Of Eels over 13 years
    You are of course right, and thanks for the insights. Let me see what I can do...
  • sasidhar
    sasidhar over 13 years
    as trashgod mentioned its working only for contiguous selections.... i have tried to store the values of the rows that are being selected, but no success here either..... :(
  • trashgod
    trashgod over 13 years
    @Hovercraft Full Of Eels: DefaultListSelectionModel has a clone() that might be helpful. Also, the data model's values are Boolean, so I think the conversion to boolean and Boolean.valueOf() can be eliminated.
  • camickr
    camickr over 13 years
    You can't use a mouse click for two different functions. The solution is a proper UI. I gave one suggestion to use a popup menu. Another solution is to use menuItems from a menu with accelerators to select/deselect. This way the user can use the mouse or the keyboard. Or you can add buttons to the UI to do the same.
  • trashgod
    trashgod over 13 years
    @Hovercraft Full Of Eels: The clone() method proved fruitless, but I used your example to implement @camikr's suggestion.
  • trashgod
    trashgod over 13 years
    I have to agree with @camickr; I added an example nearby.
  • trashgod
    trashgod over 11 years
    See comments here if sorting is enabled.
  • Sam
    Sam about 10 years
    Hi, I'm sorry for replying but I just found this a little while ago and would like some input on adding a listener for changes in the list. Right now I'm using a mouseListener, but it is firing before the list is actually updated. How could I add a listener to get updates for the list as they happen?
  • trashgod
    trashgod about 10 years
    Maybe a TableCellEditor, seen here?