4

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

有没有一种简单的方法来实现这个TableRowSorter

4

5 回答 5

5

就个人而言,我会创建一个去掉表头的单行第二张表,并将其直接放在主表下方,以营造最后一行的错觉。

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

您甚至可以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())); 
  }
}
于 2013-05-21T16:35:11.283 回答
2

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

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

然后修改排序器报告的索引和行数,如下所示...

TableRowSorter<TableModel> sorter =
    new DefaultTableRowSorter<TableModel>(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);
于 2013-06-13T17:03:32.310 回答
2

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

于 2013-05-21T15:17:21.213 回答
1

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

public class TableRowSorterTotal<MyTableModel extends TableModel> extends TableRowSorter<TableModel>
{   

    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;
    }
}
于 2014-06-06T20:26:45.467 回答
1

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

  • getRowCount()中的实现TableModel返回 的size()List<Employee>它比表中显示的行数少一。

  • getValueAt()in 的实现说明了对inTableModel的调用以及底部额外行中剩余列的一些可能的显示选择。sum()COLUMN_SALARY

  • getViewRowCount()中的实现TableRowSorter启用了额外行的显示,但排除了合理的过滤

图片

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<Employee> 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<TableModel>(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<Employee> createListEmployees() {
        List<Employee> 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<Employee> listEmployees;

        public EmployeeTableModel(List<Employee> 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;
        }
    }
}
于 2016-06-20T01:25:56.073 回答