Populating a JTable does not change the contents of the selected cell, and invokes editing on the cell; how can I work around this?

StackOverflow https://stackoverflow.com/questions/9539889

Question

I have an application which uses JTables to display data, and the cells are editable so that the user can change the data. The user can also revert the changes, or load data from an external source. However, if the user reverts/loads the data using a keyboard shortcut, so that mouse focus is not taken away from the table, the currently selected cell does not get reverted. In fact, after the refresh, the cell goes into edit mode! Then when the user navigates away from this cell, a change event is triggered, so the old value gets committed back to the data store.

I have a short example program that demonstrates this problem. It shows a table in which every cell displays the value 0. There is also a File menu with a single menu item called "Increment", which has a keyboard shortcut of Ctrl-I. Each time the Increment command is invoked, it increments the number displayed in all of the cells. To see the problem, do the following:

  1. Compile and run the program
  2. Hit Ctrl-I a bunch of times to invoke the Increment command. Notice that the cell values increment each time.
  3. Click a cell.
  4. Hit Ctrl-I a bunch of times to invoke the Increment command. Notice that all cell values increment except the one that is selected.

I have tried various methods to remove the selection from the table before refreshing it, to no avail. Neither

table.editCellAt(-1, -1);

nor

table.getSelectionModel().clearSelection();

worked, for example.

Here is the sample program:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableBug extends JFrame {
    private static final int ROW_COUNT = 3;
    private static final int COL_COUNT = 3;

    private int mDataValue = 0;
    private DefaultTableModel mTableModel;

    // Constructor
    public TableBug() {
        setTitle("TableBug");
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        // Create table model and table
        mTableModel = new DefaultTableModel();        

        for (int col = 0; col < COL_COUNT; col++) {
            mTableModel.addColumn("Value");
        }

        JTable table = new JTable(mTableModel);
        setUpTable(table);
        refresh();

        // Create menu bar
        int keyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();

        JMenu fileMenu = new JMenu("File");

        JMenuItem incrementMenuItem = new JMenuItem("Increment");
        incrementMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, keyMask));
        incrementMenuItem.addActionListener(new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                doIncrement();
            }
        });

        fileMenu.add(incrementMenuItem);

        JMenuBar mainMenuBar = new JMenuBar();
        mainMenuBar.add(fileMenu);

        // Populate GUI
        setJMenuBar(mainMenuBar);
        add(new JScrollPane(table), BorderLayout.CENTER);

        // Display window
        pack();
        setVisible(true);
    }

    // Configures the table
    private void setUpTable(JTable table) {
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

        table.getTableHeader().setReorderingAllowed(false);
        table.getTableHeader().setResizingAllowed(false);

        table.setRowSelectionAllowed(false);
    }

    // Populates the table
    private void refresh() {
        mTableModel.setRowCount(ROW_COUNT);

        for (int col = 0; col < COL_COUNT; col++) {
            for (int row = 0; row < ROW_COUNT; row++) {
                mTableModel.setValueAt(mDataValue, row, col);
            }
        }
    }

    // Handles the Increment menu item
    public void doIncrement() {
        mDataValue++;
        refresh();
    }

    // Main program
    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TableBug();
            }
        });
    }
}
Was it helpful?

Solution

In your refresh function, check if the table is being edited. If it is, get the row and column that are being edited and stop the cell editing.

private void refresh() {
    if (table.isEditing()) {
        int row = table.getEditingRow();
        int column = table.getEditingColumn();
        table.getCellEditor(row, column).stopCellEditing();
    }
 ...

To do this, you'll need to make your table variable accessible (make it a class variable).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top