开发者

Removing rows from a DefaultTableModel with a RowSorter attached doesn't remove last row

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.ad开发者_C百科d(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");
  }
}


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.


Discussed also on java.net: http://www.java.net/node/698236

It's bug #6894632 in core DefaultRowSorter: at its base (as explained in the report) is a not-so-well defined semantics of getModelRowCount(). It's fixed in SwingX DefaultSortController

/** 
 * Additionally, this implementation contains a fix for core 
 * <a href=http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6894632>Issue 6894632</a>.
 * It guarantees to only touch the underlying model during sort/filter and during 
 * processing the notification methods. This implies that the conversion and size query
 * methods are valid at all times outside the internal updates, including the critical 
 * period (in core with undefined behaviour) after the underlying model has changed and 
 * before this sorter has been notified.
 */

Unfortunately, the thread in the SwingLabs forum containing the complete analysis (was: http://www.java.net/jive/thread.jspa?threadID=77343) is no longer available after the project migration ...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜