0

I'm trying create a JTable that creates the table based on information from an ArrayList. The ArrayList is filled with information that the user enters in the GUI. Every time the user presses a JButton it should add a new row of data to the table.

After googling it seems that the best way to do this is to create a custom table model. I cannot find a decent tutorial on how to do this. I've looked at the official documentation, and a few random sites and other postings. Hopefully one of you can point me in the right direction.

Here's a picture of my current GUI. The JTable should populate the center region: My Current GUI

And here is some of my code:

 class dataModel extends AbstractTableModel
{
    private String[] columnName = {"Emp Num", "Base Pay", "Hours Worked", "Pay Amt"};

    public int getColumnCount()
    {
        return 4;
    }


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

    public Object getValueAt(int row, int col)
    {
        return new Integer(row*col);
    }
}

The class that occurs on button click.

class listener implements ActionListener
{
    @SuppressWarnings("unchecked")
    public void actionPerformed(ActionEvent e)
    {
        ArrayList empData = new ArrayList();

        int empNum = 0;
        double hourlyRate = 0;
        double hoursWorked = 0;
        try
        {
            empNum = Integer.parseInt(empNumField.getText());
        }
        catch(NumberFormatException event)
        {
            JOptionPane.showMessageDialog(null, "Invalid entry.\n\nPlease enter a number for the Employee ID.", "Error", JOptionPane.WARNING_MESSAGE);
            return;
        }
        try
        {
            hourlyRate = Double.parseDouble(basePayField.getText());
        }
        catch(NumberFormatException event)
        {
            JOptionPane.showMessageDialog(null, "Invalid entry.\n\nPlease enter a number for the Hourly Pay Rate.", "Error", JOptionPane.WARNING_MESSAGE);
            return;
        }
        try
        {
            hoursWorked = Double.parseDouble(hrsField.getText());
        }
        catch(NumberFormatException event)
        {
            JOptionPane.showMessageDialog(null, "Invalid entry.\n\nPlease enter a number for the Hours Worked.", "Error", JOptionPane.WARNING_MESSAGE);
            return;
        }

        double payAMT = calculatePay(hourlyRate, hoursWorked);

        empData.add(empNum);
        empData.add(hourlyRate);
        empData.add(hoursWorked);
        empData.add(payAMT);

    }
Justiciar
  • 356
  • 1
  • 3
  • 20
  • 1
    [This answers](http://stackoverflow.com/questions/43076871/jtable-wont-populate-when-clicking-jbutton/43078549#43078549) demonstrates a custom `TableModel` backed by an `List`, it even has the ability to add/remove removes – MadProgrammer Mar 30 '17 at 00:18
  • 1
    Based on the available information, `DefaultTableModel` will do what you want just fine – MadProgrammer Mar 30 '17 at 00:25
  • This [example](http://stackoverflow.com/a/9134371/230513) illustrates a `TableModel` having a `Map`. – trashgod Mar 30 '17 at 01:10

2 Answers2

4

You could...

Use a DefaultTableModel, which should cover your basic needs

private DefaultTableModel model;
//...
model = new DefaultTableModel(new String[] {"Emp Num", "Base Pay", "Hours Worked", "Pay Amt"}, 0);
table.setModel(model);

Then you could just add new data directly to it

class listener implements ActionListener {

    private DefaultTableModel model;

    listener(DefaultTableModel model) {
        this.model = model;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int empNum = 0;
        double hourlyRate = 0;
        double hoursWorked = 0;
        double payAMT = calculatePay(hourlyRate, hoursWorked);
        
        model.addRow(new Object[]{empNum, hourlyRate, hoursWorked, payAMT});
    }
}

This is okay, it's simple, but it does mean you need to understand the structure of the data you are presenting and doesn't allow you to manage objects which might have more you might want to use later

Now, if you want something that's more customisable...

You could...

Use a custom TableModel, first, let's start with a requirement

public interface Employee {
    public int getNumber();
    public double getHourlyRate();
    public double getHoursWorked();
    public double getPay();
}

This defines the basic expectations of the data our model can manage. Why? Because you might have different types of employees or employee implementations which have more information, which is irrelevant to this table model

Next, we implement the TableModel...

public class DefaultWorkSheetTableModel extends AbstractTableModel {

    private List<Employee> items;

    public DefaultWorkSheetTableModel() {
        items = new ArrayList<>(25);
    }

    public DefaultWorkSheetTableModel(List<Employee> items) {
        this.items = items;
    }

    @Override
    public int getRowCount() {
        return items.size();
    }

    @Override
    public int getColumnCount() {
        return 4;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        switch (columnIndex) {
            case 0: return Integer.class;
            case 1: 
            case 2: 
            case 3: return Double.class;
        }
        return Object.class;
    }

    @Override
    public String getColumnName(int column) {
        switch (column) {
            case 0: return "Emp Num";
            case 1: return "Base Pay";
            case 2: return "Hours Worked";
            case 3: return "Pay Amount";
        }
        return null;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Employee item = items.get(rowIndex);
        switch (columnIndex) {
            case 0: return item.getNumber();
            case 1: return item.getHourlyRate();
            case 2: return item.getHoursWorked();
            case 3: return item.getPay();
        }
        return null;
    }

    public void add(Employee item) {
        items.add(item);
        int row = items.indexOf(item);
        fireTableRowsInserted(row, row);
    }

    public void remove(Employee item) {
        if (items.contains(item)) {
            int row = items.indexOf(item);
            items.remove(row);
            fireTableRowsDeleted(row, row);
        }
    }

}

There's a lot going on here, but this covers all the basic requirements for allow the JTable to present the data we want.

Now when you want to add data, you create a new instance of Employee and simply add it to the model...

class listener implements ActionListener {

    private DefaultWorkSheetTableModel model;

    listener(DefaultWorkSheetTableModel model) {
        this.model = model;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int empNum = 0;
        double hourlyRate = 0;
        double hoursWorked = 0;
        //...
        DefaultEmployee employee = new DefaultEmployee(empNum, hourlyRate, hoursWorked);
        model.add(employee);
    }
}

Yes, I know I didn't pass payRate, that's because it's a calculated field specific to the implementation of Employee ;)

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for such a thorough response. This was really helpful and I finally have something working for this! – Justiciar Mar 31 '17 at 16:03
  • I used your first solution, the simpler one. The next part of my work is updating a specific cell value in the table, the payAMT variable, when the radio button selections are made. so if there current payAMT is 500, pressing the 5% radio button should increase that value in the table by 5 percent. I've tried using model.setValueAt, but getting an arrayIndexOutofBounds exeption. – Justiciar Mar 31 '17 at 16:32
  • @Justiciar Without more context is hard to know what might be wrong. Consider posting a new question which demonstrates your problem – MadProgrammer Mar 31 '17 at 22:54
1

JTable that creates the table based on information from an ArrayList.

Every time the user presses a JButton it should add a new row of data to the table.

So your real requirement is to get data from some text fields and add the data to a table.

So there is no need to use an ArrayList to do this.

You can use the DefaultTableModel for this. The DefaultTableModel supports an addRow(...) method. Using this method you can add a Vector or an Array containing the data from the 4 text fields.

So the only real change to your code would be:

//ArrayList empData = new ArrayList();
Vector empData = new Vector();

and then after you add all the data to the Vector you need to add the Vector to the DefaultTableModel:

empData.add(payAMT);
model.addRow( empData );

So of course you need to have created an empty DefaultTableModel with just the columns names first and your listener would need access to this model.

it seems that the best way to do this is to create a custom table model. I cannot find a decent tutorial on how to do this

Another option is to create a custom TableModel that contains Employee objects. In this case you could use an ArrayList to hold the individual Employee objects. For an example of creating a TableModel for this approach check out Row Table Model.

Community
  • 1
  • 1
camickr
  • 321,443
  • 19
  • 166
  • 288