对最后一行除了JTable排序

我有一个JTable ,其中最后一行是聚合所有其他行的总行。 当用户单击表上的列标题时,行按该列排序,但总行应始终位于底部。

有没有一种简单的方法来使用TableRowSorter实现它?

以下解决方案对我有用….

在您的表模型中,更新getRowCount()成员以返回少于所需的1行。

然后修改分拣机报告的索引和行数,如下所示……

 TableRowSorter sorter = new DefaultTableRowSorter(this.getModel()) { public int convertRowIndexToModel(int index) { int maxRow = super.getViewRowCount(); if (index >= maxRow) return index; return super.convertRowIndexToModel(index); } public int convertRowIndexToView(int index) { int maxRow = super.getModelRowCount(); if (index > maxRow) return index; return super.convertRowIndexToView(index); } public int getViewRowCount() { return super.getViewRowCount() + 1; } }; myTable.setRowSorter(sorter); 

就个人而言,我会创建一个删除标题的单行第二个表,并将其直接放在主表的下方,以便创建最后一行的错觉。

除了它解决你的排序问题,它也会在用户滚动主表时持续存在,这可能是一件好事,因为它是一个“总计”行。

您甚至可以将ColumnModelListener添加到主表的TableColumnModel以同步列大小调整。

编辑 :这是一般的想法:

 import java.awt.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; public class TestFrame implements Runnable { JTable mainTable; JTable fixedTable; public static void main(String[] args) { SwingUtilities.invokeLater(new TestFrame()); } public void run() { mainTable = new JTable(8, 3); mainTable.getTableHeader().setReorderingAllowed(false); mainTable.setAutoCreateRowSorter(true); for (int r = 0; r < 8; r++) { for (int c = 0; c < 3; c++) { mainTable.setValueAt((int)(Math.random()*100), r, c); } } mainTable.getColumnModel().addColumnModelListener( new TableColumnModelListener() { public void columnAdded(TableColumnModelEvent e) {} public void columnRemoved(TableColumnModelEvent e) {} public void columnMoved(TableColumnModelEvent e) {} public void columnSelectionChanged(ListSelectionEvent e) {} public void columnMarginChanged(ChangeEvent e) { synchColumnSizes(); } }); setVisibleRowCount(mainTable, 5); JScrollPane scroll = new JScrollPane(mainTable); scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); fixedTable = new JTable(1, 3); fixedTable.setValueAt("will not sort or", 0, 0); fixedTable.setValueAt("scroll but will", 0, 1); fixedTable.setValueAt("resize with main", 0, 2); JPanel p = new JPanel(new GridBagLayout()); p.setBorder(BorderFactory.createTitledBorder("Fixed Last Row")); GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.WEST; p.add(scroll, gbc); gbc.gridy = 1; p.add(fixedTable, gbc); JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().add(p, BorderLayout.CENTER); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } private void synchColumnSizes() { TableColumnModel tcmMain = mainTable.getColumnModel(); TableColumnModel tcmFixed = fixedTable.getColumnModel(); for (int i = 0; i < tcmMain.getColumnCount(); i++) { int width = tcmMain.getColumn(i).getWidth(); tcmFixed.getColumn(i).setPreferredWidth(width); } } public static void setVisibleRowCount(JTable table, int rows) { table.setPreferredScrollableViewportSize(new Dimension( table.getPreferredScrollableViewportSize().width, rows * table.getRowHeight())); } } 

有没有一种简单的方法来使用TableRowSorter实现它?

  • 并非不简单

  • 好主意, 可以为RowSorter设置标志,它的SortingKeys

  • 除String实例外,所有ClassType都有bug

@Das:也许这个版本更好,因为你不需要覆盖getRowCount(),这可能会被使用并可能导致其他函数出现问题

 public class TableRowSorterTotal extends TableRowSorter { TableRowSorterTotal(MyTableModel model) { super(model); } public int convertRowIndexToModel(int index) { int maxRow = super.getViewRowCount(); int currModel = super.convertRowIndexToModel(index); int maxModel = super.convertRowIndexToModel(maxRow-1); if(currModel == maxModel) return maxRow - 1; if(currModel > maxModel) return currModel- 1; return currModel; } public int convertRowIndexToView(int index) { int maxRow = super.getModelRowCount(); int currView= super.convertRowIndexToView(index); int maxView = super.convertRowIndexToView(maxRow-1); if(currView == maxView) return maxRow - 1; if(currView > maxView) return currView- 1; return currView; } } 

基于此示例 ,下面的完整示例示例说明了已接受答案中的方法。 尤其是,

  • TableModelgetRowCount()的实现返回Listsize() ,它比表中显示的行数一个。

  • TableModelgetValueAt()的实现说明了对COLUMN_SALARY sum()COLUMN_SALARY以及底部额外行中其余列的一些可能的显示选择。

  • TableRowSortergetViewRowCount()实现可以显示额外的行,但排除了合理的过滤 。

图片

 import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; /** * @see https://stackoverflow.com/a/37913520/230513 * @see https://stackoverflow.com/a/37892395/230513 */ public class JTableColumnTotalExample { public static void main(String[] args) { EventQueue.invokeLater(JTableColumnTotalExample::display); } public static void display() { JFrame f = new JFrame("JTable Sorting Example"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); List listEmployees = createListEmployees(); TableModel model = new EmployeeTableModel(listEmployees); JTable table = new JTable(model) { @Override public Dimension getPreferredScrollableViewportSize() { return new Dimension(500, getRowCount() * getRowHeight()); } }; table.getColumnModel().getColumn(3).setCellRenderer(new CurrencyFormatter()); TableRowSorter sorter = new TableRowSorter(model) { @Override public int convertRowIndexToModel(int index) { int maxRow = super.getViewRowCount(); if (index >= maxRow) { return index; } return super.convertRowIndexToModel(index); } @Override public int convertRowIndexToView(int index) { int maxRow = super.getModelRowCount(); if (index > maxRow) { return index; } return super.convertRowIndexToView(index); } @Override public int getViewRowCount() { return super.getViewRowCount() + 1; } }; table.setRowSorter(sorter); f.add(new JScrollPane(table), BorderLayout.CENTER); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } private static List createListEmployees() { List listEmployees = new ArrayList<>(); listEmployees.add(new Employee("Peter", "Manager", 40000)); listEmployees.add(new Employee("Paul", "Programmer", 25000)); listEmployees.add(new Employee("Mary", "Designer", 25000)); listEmployees.add(new Employee("Donald", "Leader", 30000)); listEmployees.add(new Employee("Tom", "Designer", 28000)); listEmployees.add(new Employee("Samantha", "Analyst", 50000)); listEmployees.add(new Employee("Jerome", "Programmer", 32000)); listEmployees.add(new Employee("Jonathon", "Developer", 29000)); listEmployees.add(new Employee("Kevin", "Programmer", 23000)); listEmployees.add(new Employee("Anthony", "Programmer", 23000)); listEmployees.add(new Employee("John", "Designer", 33000)); listEmployees.add(new Employee("David", "Developer", 28000)); listEmployees.add(new Employee("Harry", "Designer", 31000)); listEmployees.add(new Employee("Charles", "Programmer", 26000)); listEmployees.add(new Employee("Joseph", "Manager", 40000)); return listEmployees; } private static class EmployeeTableModel extends AbstractTableModel { private static final int COLUMN_NUM = 0; private static final int COLUMN_NAME = 1; private static final int COLUMN_JOB = 2; private static final int COLUMN_SALARY = 3; private final String[] columnNames = {"No", "Name", "Job", "Salary"}; private final List listEmployees; public EmployeeTableModel(List listEmployees) { this.listEmployees = listEmployees; int indexCount = 1; for (Employee employee : listEmployees) { employee.setIndex(indexCount++); } } @Override public int getColumnCount() { return columnNames.length; } @Override public int getRowCount() { return listEmployees.size(); } @Override public String getColumnName(int columnIndex) { return columnNames[columnIndex]; } @Override public Class getColumnClass(int columnIndex) { return getValueAt(0, columnIndex).getClass(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex == getRowCount()) { switch (columnIndex) { case COLUMN_NUM: return 999_999_999; case COLUMN_NAME: return "Total"; case COLUMN_JOB: return "Salary"; case COLUMN_SALARY: return sum(); } } Employee employee = listEmployees.get(rowIndex); switch (columnIndex) { case COLUMN_NUM: return employee.getIndex(); case COLUMN_NAME: return employee.getName(); case COLUMN_JOB: return employee.getJob(); case COLUMN_SALARY: return employee.getSalary(); default: throw new IllegalArgumentException("Invalid column index"); } } private int sum() { int sum = 0; for (int r = 0; r < getRowCount(); r++) { sum += (int) getValueAt(r, COLUMN_SALARY); } return sum; } } private static class Employee { private int index; private String name; private String job; private int salary; public Employee(String name, String job, int salary) { this.name = name; this.job = job; this.salary = salary; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public int getSalary() { return salary; } public void setSalary(int age) { this.salary = age; } } private static class CurrencyFormatter extends DefaultTableCellRenderer { private NumberFormat numberFormat = NumberFormat.getCurrencyInstance(); @Override public Component getTableCellRendererComponent(JTable jTable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent(jTable, value, isSelected, hasFocus, row, column); if (c instanceof JLabel && value instanceof Number) { JLabel label = (JLabel) c; label.setHorizontalAlignment(JLabel.RIGHT); Number num = (Number) value; String text = numberFormat.format(num); label.setText(text); } return c; } } }