Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
183 views
in Technique[技术] by (71.8m points)

java - Calculate running total in JTable

I need to populate a column in my JTable that computes a running total as is shown below.

ID   Name   Position   Salary    Total
===  ====== ========== =======   ======
1.   Peter  Programmer  40,000   40,000
2.   Paul   Manager     25,000   65,000
3.   Mary   Designer    25,000   90,000

I have 4 classes--one entity class for each employee, one table model class, one main class that extends JFrame to show the output, and one to format the numbers in the last two columns. Code is shown below. The problem I'm having is that the running total is not computed correctly. As the code is currently written, the first total is 80,000 too high, which makes me think it is adding the first employee three times before moving to the second. Each subsequent total is correctly added, but of course the final total is off by 80,000. Here is what the output looks like now:

ID   Name   Position   Salary   Total
===  ====== ========== =======   ======
1.   Peter  Programmer  40,000   120,000
2.   Paul   Manager     25,000   145,000
3.   Mary   Designer    25,000   170,000

Notice in the code below that the table can be re-sorted by column and a listener is included to re-calculate the running totals. This function needs to be zeroed out prior to re-sorting and it's not doing that now. If anyone can tell me why this is happening and how to fix it, I would be very grateful.

public class JTableRunningTotalExample extends JFrame
{
    private static final long serialVersionUID = 1L;
    private JTable table;
    private TableModel tableModel;
    private NumberCellFormatter numberCellFormatter = new NumberCellFormatter();
    private int salarySum = 0;
    private List<Employee> listEmployees;
    private Employee employee;

    public JTableRunningTotalExample()
    {
        super("JTable Sorting Example");
        createListEmployees();
        tableModel = new EmployeeTableModel(listEmployees);
        table = new JTable(tableModel);
        for (int j=3; j<5; j++)
        {
            table.getColumnModel().getColumn(j).setCellRenderer(numberCellFormatter);
        }

        TableRowSorter<TableModel> sorter = new TableRowSorter<>(table.getModel());
        table.setRowSorter(sorter);
        List<RowSorter.SortKey> sortKeys = new ArrayList<>();

        int columnIndexToSort = 0;
        sortKeys.add(new RowSorter.SortKey(columnIndexToSort, SortOrder.ASCENDING));

        sorter.setSortKeys(sortKeys);
        sorter.sort();
        sorter.addRowSorterListener(new RowSorterListener()
        {
            public void sorterChanged(RowSorterEvent evt)
            {
                int columnIndex = 0;
                salarySum = 0;
                for (int i = 0; i < table.getRowCount(); i++)
                {
                    table.setValueAt(i + 1, i, columnIndex);
                }
            }
        });
        add(new JScrollPane(table), BorderLayout.CENTER);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void createListEmployees()
    {
        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));
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                JTableRunningTotalExample frame = new JTableRunningTotalExample();
                frame.setVisible(true);
                frame.setLocationRelativeTo(null);
            }
        });
    }
}

*****Table Model class*****

public class EmployeeTableModel extends AbstractTableModel
{
    private static final long serialVersionUID = 1L;
    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 static final int COLUMN_SUM = 4;

    private String[] columnNames = { "No #", "Name", "Job", "Salary", "Total Payroll" };
    private List<Employee> listEmployees;
    private int salarySum = 0;
    private Employee employee;

    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)
    {
        if (listEmployees.isEmpty())
        {
            return Object.class;
        }
        return getValueAt(0, columnIndex).getClass();
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        employee = listEmployees.get(rowIndex);
        Object returnValue = null;
        switch (columnIndex)
        {
            case COLUMN_NUM:
                returnValue = employee.getIndex();
                break;
            case COLUMN_NAME:
                returnValue = employee.getName();
                break;
            case COLUMN_JOB:
                returnValue = employee.getJob();
                break;
            case COLUMN_SALARY:
                returnValue = employee.getSalary();
                break;
            case COLUMN_SUM:
                salarySum = salarySum + employee.getSalary();
                returnValue = salarySum ;
                break;
            default:
                throw new IllegalArgumentException("Invalid column index");
        }
        return returnValue;
    }
}

*****Empoloyee entity class*****

public 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;
    }
}

*****Cell Renderer class*****

public class NumberCellFormatter extends DefaultTableCellRenderer
{
    private static final long serialVersionUID = 1L;
    private NumberFormat numberFormat = new DecimalFormat("#,###,###");
    private double formattedNumber;

    public double setNumberFormat(String number)
    {
        formattedNumber = Double.parseDouble(numberFormat.format(number));
        return formattedNumber;
    }
    @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);
            label.setForeground(num.doubleValue() < 0 ? Color.RED : Color.BLACK);
        }
        return c;
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As discussed in How to Use Tables: Sorting and Filtering, "When using a sorter, always remember to translate cell coordinates." Because JTable sorting occurs in the view, use view coordinates when calculating the partial sum for COLUMN_SUM in your implementation of getValueAt(), and disable sorting for that column as shown here.

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
    Employee employee = listEmployees.get(rowIndex);
    switch (columnIndex) {
        …
        case COLUMN_SUM:
            return partialSum(rowIndex);
        …
    }
}

private int partialSum(int row) {
    int viewRow = table.convertRowIndexToView(row);
    int viewCol = table.convertColumnIndexToView(COLUMN_SALARY);
    int sum = (int) table.getValueAt(viewRow, viewCol);
    for (int i = 0; i < viewRow; i++) {
        sum += (int) table.getValueAt(i, viewCol);
    }
    return sum;
}

image

As tested:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultRowSorter;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;

/** @see https://stackoverflow.com/a/37892395/230513 */
public class JTableRunningTotalExample extends JFrame {

    private final NumberCellFormatter numberCellFormatter = new NumberCellFormatter();

    public JTableRunningTotalExample() {
        super("JTable Sorting Example");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JTable table = new JTable() {
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(500, getRowCount() * getRowHeight());
            }
        };
        List<Employee> listEmployees = createListEmployees();
        TableModel model = new EmployeeTableModel(table, listEmployees);
        table.setModel(model);
        table.setAutoCreateRowSorter(true);
        DefaultRowSorter sorter = (DefaultRowSorter) table.getRowSorter();
        sorter.setSortable(4, false);
        for (int j = 3; j < 5; j++) {
            table.getColumnModel().getColumn(j).setCellRenderer(numberCellFormatter);
        }
        add(new JScrollPane(table), BorderLayout.CENTER);
        pack();
    }

    private 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;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JTableRunningTotalExample frame = new JTableRunningTotalExample();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    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 static final int COLUMN_SUM = 4;

        private String[] columnNames = {"No", "Name", "Job", "Salary", "Total Payroll"};
        JTable table;
        private List<Employee> listEmployees;

        public EmployeeTableModel(JTable table, List<Employee> listEmployees) {
            this.table = table;
            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) {
            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();
                case COLUMN_SUM:
                    return partialSum(rowIndex);
                default:
                    throw new IllegalArgumentException("Invalid column index");
            }
        }

        private int partialSum(int row) {
            int viewRow = table.convertRowIndexToView(row);
            int viewCol = table.convertColumnIndexToView(COLUMN_SALARY);
            int sum = (int) table.getValueAt(viewRow, viewCol);
            for (int i = 0; i < viewRow; i++) {
                sum += (int) table.getValueAt(i, viewCol);
            }
            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 NumberCellFormatter extends DefaultTableCellRenderer {

        private static final long serialVersionUID = 1L;
        private NumberFormat numberFormat = NumberFormat.getCurrencyInstance();
        private double formattedNumber;

        public double setNumberFormat(String number) {
            formattedNumber = Double.parseDouble(numberFormat.format(number));
            return formattedNumber;
        }

        @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);
                label.setForeground(num.doubleValue() < 0 ? Color.RED : Color.BLACK);
            }
            return c;
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...