views:

797

answers:

1

I'm somewhat new to Java, and especially new to tables, and I'm having a bit of trouble with one particular task.

I have a JTable that uses a custom table model that extends DefaultTableModel, and I've attached a TableRowSorter to the table. The sample application below has two buttons--one will load rows into the table and one will remove all selected rows from the table.

For some reason, if you select the last row in the table along with any other row, when you click the "Remove" button it will remove all selected rows except the last row. You can remove any other combination of rows and it works fine.

What's more, if you first click on the column header to sort the rows (even if the order of rows doesn't change), it will work correctly. If I add a line to explicitly sort the rows in the table after loading it, the problem "goes away", but I'd like to know why what I'm doing is incorrect.

To see the behavior, click the "Load" button to populate the table, select all of the rows in the table, then click the "Remove" button. It will remove all rows except the last one.

As the call to println shows, the first iteration through the loop reduces the selected row count by two. This behavior is consistent no matter how many rows you have in the table, but only if you've selected the last row in the table.

I'm using Java version 1.6.0_16. Any ideas as to what I'm doing wrong?

Thanks,

Joe

import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import java.util.Arrays;

public class TableTest
extends JFrame
{
  private JTable widgetTable;
  private WidgetTableModel widgetTableModel;

  public static void main(String[] args)
  {
    TableTest frame = new TableTest();
    frame.setSize(600, 400);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public TableTest()
  {
    this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    EventQueue.invokeLater(new Runnable() { public void run() { createUI(); } });
  }

  private void createUI()
  {
    this.setLayout(new BorderLayout());

    JButton loadButton = new JButton("Load");
    loadButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        loadPerformed();
      }
    });

    this.add(loadButton, BorderLayout.NORTH);

    widgetTableModel = new WidgetTableModel();
    widgetTable = new JTable(widgetTableModel);
    widgetTable.setRowSorter(new TableRowSorter<WidgetTableModel>(widgetTableModel));
    this.add(new JScrollPane(widgetTable), BorderLayout.CENTER);

    JButton removeButton = new JButton("Remove");
    removeButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        removePerformed();
      }
    });

    this.add(removeButton, BorderLayout.SOUTH);
  }

  private void loadPerformed()
  {
    widgetTableModel.addRow(new Object[] {"Widget 1"});
    widgetTableModel.addRow(new Object[] {"Widget 2"});
    widgetTableModel.addRow(new Object[] {"Widget 3"});
    widgetTableModel.addRow(new Object[] {"Widget 4"});
    widgetTableModel.addRow(new Object[] {"Widget 5"});
  }

  private void removePerformed()
  {
    int selectedRow = widgetTable.getSelectedRow();

    while (selectedRow >= 0) {
      System.out.println("selectedRowCount=" + widgetTable.getSelectedRowCount());
      int modelRow = widgetTable.convertRowIndexToModel(selectedRow);
      widgetTableModel.removeRow(modelRow);
      selectedRow = widgetTable.getSelectedRow();
    }
  }
}


class WidgetTableModel
extends DefaultTableModel
{
  public WidgetTableModel()
  {
    this.addColumn("Column 1");
  }
}
A: 

Change the "while" to an "if". Select all rows and then click the button. For some reason the last row loses its selection. I don't know why.

I find remove logic should generally be done from the last row down to 0. This way you don't have to worry about the row index values changes as a row is removed. So you need to use the getSelectedRows() method and iterate through the array in reverse order. Although, I must admit I've never done this on a sorted table so I'm not sure if it will cause a problem or not.

camickr
Iterating through the selected rows in reverse order should work, but it seems unnecessary.The way the example works now should be just as dependable--always get the first selected row in the table. That also removes the need to keep track of index changes. The problem seems to be the "bug" (or whatever the source) that causes that last row to be deselected.