Question

I'm using ListSelectionListener to update my JTextField (countryTxt) from selected row.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

public class App {

    JFrame frame = new JFrame();
    JTable table = new JTable();
    DefaultTableModel model = new DefaultTableModel(new Object[][] {},
            new String[] { "Country", "City", "Street" });
    JButton button = new JButton("Remove");
    JTextField countryTxt = new JTextField();

    int row;

    public App() {
        table.setModel(model);
        data();
        table.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        if (!e.getValueIsAdjusting()) {
                            row = table.getSelectedRow();
                            countryTxt.setText((String) model
                                    .getValueAt(row, 0));
                        }
                    }
                });
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                model.removeRow(row);
            }
        });
        frame.add(countryTxt,BorderLayout.NORTH);
        frame.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.add(button, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
    }

    public void data() {
        model.addRow(new String[] { "USA", "New York", "First street" });
        model.addRow(new String[] { "Russia", "Moscow", "Second street" });
        model.addRow(new String[] { "Japan", "Osaka", "Osaka street" });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new App();
            }
        });
    }
}

But when I select a row and click a button it trows me and ArrayIndexOutOfBounds exception. When I don't select a row in my table and click a button everything works fine. Obviously I can delete a row when valueChanged event is not triggered. So my question is: How to delete a row after valueChanged event is triggered. Thanks in advance.

Was it helpful?

Solution

I had to track down a similar problem involving list removal a while ago. The main issue here is that the button listener's call to model.removeRow(row) was sending a valueChanged event to the model's selection listener, which would then attempt to update the text field using a nonexistent selection (i.e. a list index of -1). I've made these fixes to your code, and the relevant sections are marked with comments. This code allows items to be selected/deleted without throwing an exception.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

public class App {
    JFrame frame = new JFrame();
    DefaultTableModel model = new DefaultTableModel(new Object[][] {},
            new String[] { "Country", "City", "Street" });
    JTable table = new JTable(model);
    JButton button = new JButton("Remove");
    JTextField countryTxt = new JTextField();

    public App() {
        data();
        table.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        if (!e.getValueIsAdjusting()) {
                            // get the current selected row
                            int i = table.getSelectedRow();
                            // if there is a selected row, update the text field
                            if(i >= 0) {
                               countryTxt.setText((String) model
                                    .getValueAt(i, 0));
                            }
                        }
                    }
                });
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                // get the current selected row
                int i = table.getSelectedRow();
                // if there's no selection, but there are some rows,
                // we'll just delete the first row
                if(i < 0 && model.getRowCount() > 0) {
                   i = 0;
                }

                // if we have a valid row to delete, do the deletion
                if(i >= 0) {
                    countryTxt.setText("");
                    model.removeRow(i);
                    table.revalidate();
                }
            }
        });
        frame.add(countryTxt,BorderLayout.NORTH);
        frame.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.add(button, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
    }

    public void data() {
        model.addRow(new String[] { "USA", "New York", "First street" });
        model.addRow(new String[] { "Russia", "Moscow", "Second street" });
        model.addRow(new String[] { "Japan", "Osaka", "Osaka street" });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new App();
            }
        });
    }
}

OTHER TIPS

Have a look at the javadoc of the getLeadSelectionIndex() method

Return the second index argument from the most recent call to setSelectionInterval(), addSelectionInterval() or removeSelectionInterval()

This is not what you expect. You better use the JTable#getSelectedRow() which of course still requires you to check whether it is different from -1 .

A few observations:

  • Selecting a row via keyboard or mouse updates the countryTxt field correctly.

  • You can use Control>-Tab to tab out of the table and back to your panel.

  • Don't use setBounds(); do use pack().

  • I tested your example without MigLayout, but I don't think that's relevant to your findings.

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