Search code examples
javaswingjtablekeystroke

Error when deleting a row in a row sorted JTable


I get an error when I remove a row a sorted row in a JTable.

The error appears only when the table is sorted, and I know where the error source is: the method updateRowHeights() in the tableChanged causes an Exception java.lang.ArrayIndexOutOfBoundsException.

I guess that the line int rowHeight = table.getRowHeight(); causes the problem, but I don't know why.

Here is my code:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.*;

public class TableExample {

String [] title = new String []      {"Title A", "Title B"};
Object [][] data = new String [][]  {{"aaaaaaaaaaaa aaaaaa aaaaaaa", "bbbbbbbb bbbb bbbbbb bbbbbb"},
                                     {"cccccccccc cccccccc ccccccc", "ddddddd ddd dddddddd dddddd"},
                                     {"eeeeeeeeee eeeeeeee eeeeeee", "fffffff ffff ffffff fffffff"}};


private JTable table;
private JFrame frame;
private DefaultTableModel model;
private JScrollPane pane1;

TableExample() {} //constructor

public JPanel createTable() {

    JPanel panel = new JPanel();

    //creating tables and table models
    model = new DefaultTableModel(data, title);       
    table = new JTable(model);

    table.getModel().addTableModelListener(new TableModelListener() {
        @Override
        public void tableChanged(TableModelEvent e) {
            updateRowHeights();             
        }           
    });

    //enable table sorting
    table.setAutoCreateRowSorter(true);

    pane1 = new JScrollPane(table);
    pane1.setPreferredSize(new Dimension(300,300));        

    updateRowHeights();     

    panel.add(pane1);

    //delete a row after del keystroke
    keyBindings();

    return panel;
}

void showTable() {
    //create and show frame     
    JPanel testPanel = createTable();
    frame = new JFrame();       
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(testPanel);              
    frame.pack();
    frame.setVisible(true);
}//showTable

void updateRowHeights() {
    for (int row = 0; row < table.getRowCount(); row++) {
        int rowHeight = table.getRowHeight();            
            Component comp = table.prepareRenderer(table.getCellRenderer(row, 1), row, 1);                
            rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);                
        table.setRowHeight(row, rowHeight);
    }
}

void keyBindings() {
    int condition = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT; 
    InputMap inputMap = table.getInputMap(condition);
    ActionMap actionMap = table.getActionMap(); 
    inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete");
    actionMap.put("delete", new AbstractAction() {  
        public void actionPerformed(ActionEvent e) {  
            int row = table.getSelectedRow();
            model.removeRow(row);
        }
    });
}

public static void main(String[] args) {
    TableExample example = new TableExample();  
    example.showTable();
}//main 

}//TableExample

How can I solve this problem?


Solution

  • As noted here, "When using a sorter, always remember to translate cell coordinates." In your delete action, for example,

    row = table.convertRowIndexToModel(row);
    

    A similar problem afflicts updateRowHeights(), although I did not pursue this.

    Also consider overriding getPreferredScrollableViewportSize(), instead of calling setPreferredSize(); more details here.

    As tested:

    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    
    import javax.swing.*;
    import javax.swing.event.TableModelEvent;
    import javax.swing.event.TableModelListener;
    import javax.swing.table.*;
    
    public class TableExample {
    
        String[] title = new String[]{"Title A", "Title B"};
        Object[][] data = new String[][]{
            {"aaaaaaaaaaaa aaaaaa aaaaaaa", "bbbbbbbb bbbb bbbbbb bbbbbb"},
            {"cccccccccc cccccccc ccccccc", "ddddddd ddd dddddddd dddddd"},
            {"eeeeeeeeee eeeeeeee eeeeeee", "fffffff ffff ffffff fffffff"}};
    
        private JTable table;
        private JFrame frame;
        private DefaultTableModel model;
        private JScrollPane pane1;
    
        public JPanel createTable() {
            JPanel panel = new JPanel();
            //creating tables and table models
            model = new DefaultTableModel(data, title);
            table = new JTable(model);
            //enable table sorting
            table.setAutoCreateRowSorter(true);
            pane1 = new JScrollPane(table);
            panel.add(pane1);
            //delete a row after del keystroke
            keyBindings();
            return panel;
        }
    
        void showTable() {
            //create and show frame     
            JPanel testPanel = createTable();
            frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(testPanel);
            frame.pack();
            frame.setVisible(true);
        }//showTable
    
        void keyBindings() {
            int condition = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
            InputMap inputMap = table.getInputMap(condition);
            ActionMap actionMap = table.getActionMap();
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete");
            actionMap.put("delete", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int row = table.getSelectedRow();
                    row = table.convertRowIndexToModel(row);
                    model.removeRow(row);
                }
            });
        }
    
        public static void main(String[] args) {
            TableExample example = new TableExample();
            example.showTable();
        }//main 
    
    }//TableExample