7

我已经弄清楚如何JTable正确排序 a ,但我不知道如何让它在更改表格单元格时自动更新排序顺序。现在,我有这个(诚然很长)代码,主要基于 Java 教程的How to Use Tables中的代码。我已经突出显示了我所做的更改// ADDED。在这种情况下,新添加的值会正确排序,但是当我进入编辑值时,即使我调用fireTableCellUpdated?

简而言之,当模型中的数据值发生变化时,如何让表格重新排序?

/*
 * Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
 * See the standard BSD license.
 */ 

package components;

/*
 * TableSortDemo.java requires no other files.
 */

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

public class TableSortDemo extends JPanel {
    private boolean DEBUG = false;

    public TableSortDemo() {
        super();
        setLayout(new BoxLayout(TableSortDemo.this, BoxLayout.PAGE_AXIS));
        final MyTableModel m = new MyTableModel();
        JTable table = new JTable(m);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);
        table.setAutoCreateRowSorter(true);

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Add the scroll pane to this panel.
        add(scrollPane);

        // ADDED: button to add a value
        JButton addButton = new JButton("Add a new value");
        addButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                m.addValue(
                        JOptionPane.showInputDialog(
                                TableSortDemo.this, "Value?"));
            }
        });

        // ADDED button to change a value
        JButton setButton = new JButton("Change a value");
        setButton.addActionListener(new ActionListener() {
            /* (non-Javadoc)
             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
             */
            public void actionPerformed(ActionEvent e) {
                m.setValueAt(
                        JOptionPane.showInputDialog(
                                TableSortDemo.this, "Value?"),
                        Integer.parseInt(
                                JOptionPane.showInputDialog(
                                        TableSortDemo.this, "Which?")), 0);
            }
        });
        add(addButton);
        add(setButton);
    }

    class MyTableModel extends AbstractTableModel {
        private static final long serialVersionUID = -7053335255134714625L;
        private String[] columnNames = {"Column"};
        // ADDED data as mutable ArrayList
        private ArrayList<String> data = new ArrayList<String>();

        public MyTableModel() {
            data.add("Anders");
            data.add("Lars");
            data.add("Betty");
            data.add("Anna");
            data.add("Jon");
            data.add("Zach");
        }

        // ADDED
        public void addValue(Object v) {
            data.add(v.toString());
            int row = data.size() - 1;
            fireTableRowsInserted(row, row);
        }

        public int getColumnCount() {
            return columnNames.length;
        }

        public int getRowCount() {
            return data.size();
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data.get(row) + " " + row;
        }

        /*
         * JTable uses this method to determine the default renderer/
         * editor for each cell.  If we didn't implement this method,
         * then the last column would contain text ("true"/"false"),
         * rather than a check box.
         */
        public Class<String> getColumnClass(int c) {
            return String.class;
        }

        /*
         * Don't need to implement this method unless your table's
         * editable.
         */
        public boolean isCellEditable(int row, int col) {
            //Note that the data/cell address is constant,
            //no matter where the cell appears onscreen.
            if (col < 2) {
                return false;
            } else {
                return true;
            }
        }

        /*
         * Don't need to implement this method unless your table's
         * data can change.
         */
        public void setValueAt(Object value, int row, int col) {
            if (DEBUG) {
                System.out.println("Setting value at " + row + "," + col
                                   + " to " + value
                                   + " (an instance of "
                                   + value.getClass() + ")");
            }

            data.set(row, value.toString());

            // ADDED: uncommented this line, despite warnings to the contrary
            fireTableCellUpdated(row, col);

            if (DEBUG) {
                System.out.println("New value of data:");
                printDebugData();
            }
        }

        private void printDebugData() {
            int numRows = getRowCount();
            int numCols = getColumnCount();

            for (int i=0; i < numRows; i++) {
                System.out.print("    row " + i + ":");
                for (int j=0; j < numCols; j++) {
                    System.out.print("  " + data.get(i));
                }
                System.out.println();
            }
            System.out.println("--------------------------");
        }
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("TableSortDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        TableSortDemo newContentPane = new TableSortDemo();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}
4

4 回答 4

18

这采取了两步解决方案:

首先,我对数据更改进行了 TableSorter 排序,方法是使用它而不是autoCreateRowSorter

sorter = new TableRowSorter<MyTableModel>(m);
table.setRowSorter(sorter);
sorter.setSortsOnUpdates(true);

然后,我不得不更改更新方法来更新整个表。和只会重绘被更新fireTableCellUpdatedfireTableRowsUpdated特定行,而不是整个表(这意味着你会得到一个重复出现的条目,一旦它被重绘就会改变。所以,我改变了

fireTableCellUpdated(row, col);

fireTableRowsUpdated(0, data.size() - 1);

现在它可以正确排序,即使数据发生变化,并且选择被保留。

于 2009-06-15T19:11:31.820 回答
4

它是JTable 上长期存在的错误,于 2007 年报告(惊讶于它没有修复,甚至在 jdk7 中也没有)

如果不会过多地降低性能(由于触发频繁的完整手段),则对所有行进行更新是一个合理的快速修复。对于无所畏惧的人,这里是对 JTable 的部分修复 - 部分修复,因为尚未捕获所有可能的场景。这就是它从未进入 JXTable 的原因(或者我当时可能有其他优先事项 :-)

public static class JTableRepaintOnUpdate extends JTable {

  private UpdateHandler beforeSort;

  @Override
  public void sorterChanged(RowSorterEvent e) {
      super.sorterChanged(e);
      maybeRepaintOnSorterChanged(e);
  } 

  private void beforeUpdate(TableModelEvent e) {
      if (!isSorted()) return;
      beforeSort = new UpdateHandler(e);
  }

  private void afterUpdate() {
      beforeSort = null;
  }

  private void maybeRepaintOnSorterChanged(RowSorterEvent e) {
      if (beforeSort == null) return;
      if ((e == null) || (e.getType() != RowSorterEvent.Type.SORTED)) return;
      UpdateHandler afterSort = new UpdateHandler(beforeSort);
      if (afterSort.allHidden(beforeSort)) {
          return;
      } else if (afterSort.complex(beforeSort)) {
          repaint();
          return;
      }
      int firstRow = afterSort.getFirstCombined(beforeSort);
      int lastRow = afterSort.getLastCombined(beforeSort);
      Rectangle first = getCellRect(firstRow, 0, false);
      first.width = getWidth();
      Rectangle last = getCellRect(lastRow, 0, false);
      repaint(first.union(last));
  }

  private class UpdateHandler {
      private int firstModelRow;
      private int lastModelRow;
      private int viewRow;
      private boolean allHidden;

      public UpdateHandler(TableModelEvent e) {
          firstModelRow = e.getFirstRow();
          lastModelRow = e.getLastRow();
          convert();
      }

      public UpdateHandler(UpdateHandler e) {
          firstModelRow = e.firstModelRow;
          lastModelRow = e.lastModelRow;
          convert();
      }

      public boolean allHidden(UpdateHandler e) {
          return this.allHidden && e.allHidden;
      }

      public boolean complex(UpdateHandler e) {
          return (firstModelRow != lastModelRow);
      }

      public int getFirstCombined(UpdateHandler e) {
          if (allHidden) return e.viewRow;
          if (e.allHidden) return viewRow;
          return Math.min(viewRow, e.viewRow);
      }

      public int getLastCombined(UpdateHandler e) {
          if (allHidden || e.allHidden) return getRowCount() - 1;
          return Math.max(viewRow, e.viewRow);

      }

      private void convert() {
          // multiple updates
          if (firstModelRow != lastModelRow) {
              // don't bother too much - calculation not guaranteed to do anything good
              // just check if the all changed indices are hidden
              allHidden = true;
              for (int i = firstModelRow; i <= lastModelRow; i++) {
                  if (convertRowIndexToView(i) >= 0) {
                      allHidden = false;
                      break;
                  }
              }
              viewRow = -1;
              return;
          }
          // single update
          viewRow = convertRowIndexToView(firstModelRow);
          allHidden = viewRow < 0;
      }

  }

  private boolean isSorted() {
      // JW: not good enough - need a way to decide if there are any sortkeys which
      // constitute a sort or any effective filters  
      return getRowSorter() != null;
  }

  @Override
  public void tableChanged(TableModelEvent e) {
      if (isUpdate(e)) {
          beforeUpdate(e);
      }
      try {
          super.tableChanged(e);
      } finally {
          afterUpdate();
      }
  }

  /**
   * Convenience method to detect dataChanged table event type.
   * 
   * @param e the event to examine. 
   * @return true if the event is of type dataChanged, false else.
   */
  protected boolean isDataChanged(TableModelEvent e) {
      if (e == null) return false;
      return e.getType() == TableModelEvent.UPDATE && 
          e.getFirstRow() == 0 &&
          e.getLastRow() == Integer.MAX_VALUE;
  }

  /**
   * Convenience method to detect update table event type.
   * 
   * @param e the event to examine. 
   * @return true if the event is of type update and not dataChanged, false else.
   */
  protected boolean isUpdate(TableModelEvent e) {
      if (isStructureChanged(e)) return false;
      return e.getType() == TableModelEvent.UPDATE && 
          e.getLastRow() < Integer.MAX_VALUE;
  }

  /**
   * Convenience method to detect a structureChanged table event type.
   * @param e the event to examine.
   * @return true if the event is of type structureChanged or null, false else.
   */
  protected boolean isStructureChanged(TableModelEvent e) {
      return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW;
  }

}
于 2011-11-16T10:30:32.547 回答
1

对其进行排序的最简单方法可能是调用 fireTableDataChanged() 而不是 fireTableCellUpdated()。

于 2009-06-15T18:31:19.410 回答
-1

您必须在这里做几件事。

  1. 由于表模型包装了您的集合,因此它必须是可排序的。这意味着您的对象(行)必须实现 Comparable 接口,以便可以正确排序集合。
  2. 在您的 setValueAt 方法中,您必须更新适当的属性并使用 Collections.sort 重新收集集合。然后,很明显,您必须调用 fireTableDataChanged 让 table 知道它需要重绘。
  3. 假设在添加数据时也会发生同样的事情。
  4. 删除数据后,您不必采取措施,但仍然必须触发TableDataChanged
  5. 如果您的集合很大,您可能会考虑最初将数据添加到适当的位置,而不是求助。

希望这可以帮助

于 2009-06-15T17:34:10.837 回答