简体   繁体   中英

TableModelListener and addRow interference in JTable/DefaultTableModel

Is TableModelListener (line 87/ HERE-1) interfering with my addRow method (line 139, HERE-2) for my JTable? If so how do I fix it?

This code will compile but an error is thrown when I use the "add row" button. The error is: (the stack trace is a lot longer but I've reduced it to the lines regarding my class and not the java API)

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException:

at HPLC.addRow(HPLC.java:142)

at HPLC.lambda$new$1(HPLC.java:109)

This has only been an issue since I've added the TableModelListener.

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

class HPLC extends JFrame
{
    public static void main (String [] args)
    {
        new HPLC();
    }
    HPLC()
    {
        //create window
        JFrame frame = new JFrame();
        frame.setTitle("HPLC Calculator");
        frame.setSize(500,750);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());

        //row 0
        JLabel title = new JLabel("HPLC Gradient Calculator");
        gridBagAdd(panel, title, 0,0,4,1, GridBagConstraints.CENTER);

        //row 1
        JLabel flowLabel = new JLabel("Flow rate");
        gridBagAdd(panel, flowLabel, 0,1,1,1, GridBagConstraints.CENTER);

        JTextField flow = new JTextField(5);
        gridBagAdd(panel, flow, 1,1,1,1, GridBagConstraints.CENTER);

        JLabel mlMinLabel = new JLabel("ml/min");
        gridBagAdd(panel, mlMinLabel, 2,1,1,1, GridBagConstraints.CENTER);

        //row 2
        JLabel numInjLabel = new JLabel("Number of injections");
        gridBagAdd(panel, numInjLabel, 0,2,1,1, GridBagConstraints.CENTER);

        JTextField numInj = new JTextField(5);
        gridBagAdd(panel, numInj, 1,2,1,1, GridBagConstraints.CENTER);

        //row 3
        JLabel volALabel = new JLabel("Volume of A");
        gridBagAdd(panel, volALabel, 0,3,1,1, GridBagConstraints.CENTER);

        JTextField volA = new JTextField(5);
        volA.setEditable(false);
        gridBagAdd(panel, volA, 1,3,1,1, GridBagConstraints.CENTER);

        JLabel volBLabel = new JLabel("Volume of B");
        gridBagAdd(panel, volBLabel, 2,3,1,1, GridBagConstraints.CENTER);

        JTextField volB = new JTextField(5);
        volB.setEditable(false);
        gridBagAdd(panel, volB, 3,3,1,1, GridBagConstraints.CENTER);

        //row 4
        JButton calculate = new JButton("calculate");
        gridBagAdd(panel, calculate, 0,4,1,1, GridBagConstraints.CENTER);

        JButton addRow = new JButton("add row");
        gridBagAdd(panel, addRow, 1,4,1,1, GridBagConstraints.CENTER);

        JButton removeRow = new JButton("remove row");
        gridBagAdd(panel, removeRow, 2,4,1,1, GridBagConstraints.CENTER);

        //the gradient table
        String[] columns = {"Time", "%A", "%B"};
        DefaultTableModel model = new DefaultTableModel(columns, 2)
        {
            public boolean isCellEditable(int row, int column)
            //this overrides the default method to suit these conditions to make the 3rd column read only
            {
                if (column == 2)
                    return false;
                return true;
            }
        };
        JTable table = new JTable(model);
        table.setFillsViewportHeight(true);
        JScrollPane tablePane = new JScrollPane(table);
        gridBagAdd(panel, tablePane, 0,5,4,4, GridBagConstraints.CENTER);
        table.getModel().addTableModelListener(new TableModelListener()
   //HERE-1 {
                public void tableChanged(TableModelEvent e)
                //overriding the TableModelListener method
                {
                    //the basics in order to get the table cell
                    int row = e.getFirstRow();
                    int column = e.getColumn();
                    TableModel model = (TableModel)e.getSource();
                    String ColumnName = model.getColumnName(column);
                    Object data = model.getValueAt(row, column);

                    //what i do with that cell
                    volB.setText(data.toString());
                }   
            });

        //button methods
        calculate.addActionListener(e -> calculate(model, volA));
        addRow.addActionListener(e -> addRow(model));
        removeRow.addActionListener(e -> removeRow(model));

        //make visible
        frame.add(panel);
        frame.setVisible(true);
    }
    //method for placement of components
    private void gridBagAdd(JPanel p, JComponent c, int x, int y, int width, int height, int align)
    {
        GridBagConstraints gc = new GridBagConstraints();
        gc.gridx = x;
        gc.gridy = y;
        gc.gridwidth = width;
        gc.gridheight = height;
        gc.weightx = 100;
        gc.weighty = 100;
        gc.insets = new Insets(5,5,5,5);
        gc.anchor = align;
        gc.fill = GridBagConstraints.NONE;
        p.add(c, gc);
    }
    //button method execution
    private void calculate(DefaultTableModel model, JTextField volA)
    {
            int rowCount = model.getRowCount();
            String msg = rowCount + " rows";
            volA.setText(msg);
    }
    private void addRow(DefaultTableModel model)
    {
        int colCount = model.getColumnCount();
//HERE-2 Object [] newRow = new Object[colCount];
        model.addRow(newRow);
    }
    private void removeRow(DefaultTableModel model)
    {
        int rowCount = model.getRowCount();
        if (rowCount > 2)
            model.removeRow(rowCount -1);
    }
}

What I'd like my table to do, is to create an array based on the contents of the table, in order to perform a calculation. The TableModelListener has been added so that the %B column will auto complete to give a total of 100%.

I'm self taught and this is the first time anyone has ever seen any of my code. If you have any advise on how to improve my method of writing I'd be happy to hear it.

You're adding a new row, and so the TableModelListener will return the ALL_COLUMNS constant in your event for the column property, a value which is -1 reference .

int column = e.getColumn(); // here

and then trying to use that -1 value here:

Object data = model.getValueAt(row, column);

which obviously won't work.

The solution: check the columns value in your listener before using it. Perhaps you will want to use an int literal for your column value.

The TableModelListener has been added so that the %B column will auto complete to give a total of 100%

The event will tell you why it was generated. It looks to me like you only want to execute your code when you change a value in a cell.

So the code should be something like:

public void tableChanged(TableModelEvent e)
{
    if (e.getType() == TableModelEvent.UPDATE)
    {
        // add processing here
    }
}

For a working example check out: JTable -> TableModeListener

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM