1

I have a problem with my JTable. My JTable displays content of a database. One database table has the name category. Every category is displayed in the JComboBox. If I click on a category it should update the table content.

Here is a short snipped of my code for you, so it is easier to help me. The code should be runable:

(TestClass - Main)

package test;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;

public class TestClass implements ActionListener{

    String[] header = {"head", "head", "head"};
    Object[][] data = {{Boolean.FALSE, "text", "text"}, {Boolean.FALSE, "text", "text"}, {Boolean.FALSE, "text", "text"}};

    LinkedList<String> newdata = new LinkedList<String>();

    String[] combolist = {"apple", "banana", "cranberry"};

    JComboBox<String> combobox = new JComboBox<String>(combolist);
    JTable table = new JTable(new TestTableModel(data, header));
    JFrame frame = new JFrame();
    JPanel panel = new JPanel(new GridLayout(1, 0, 1, 0));

    public TestClass() {
        combobox.addActionListener(this);
        panel.add(combobox);

        frame.add(panel, BorderLayout.NORTH);
        frame.add(new JScrollPane(table), BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
        frame.pack();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == this.combobox) {
            JComboBox<String> combobox = this.combobox;

            newdata.add("Test1");
            newdata.add("Test2");

            TestTableModel model = (TestTableModel) table.getModel();

            int i = 0;
            for (String text : newdata) {
                data[i][0] = Boolean.TRUE;
                data[i][1] = text;
                data[i][2] = text;
                i++;
            }

            model.setData(data);
        }
    }

    public static void main(String[] args) {
        new TestClass();
    }
}

(TestTableModel - AbstractTableModel if required (you need it to execute the code!))

package test;

import javax.swing.table.AbstractTableModel;

public class TestTableModel extends AbstractTableModel {
    private static final long serialVersionUID = 5044877015250409328L;

    private String[] header;
    private Object[][] data;

    public TestTableModel(Object[][] data, String[] header) {
        this.header = header;
        this.data = data;
    }

    public void setData(Object[][] data) {
        this.data = data;
        fireTableDataChanged();
    }

    @Override
    public Class<?> getColumnClass(int column) {
        if (column == 0) {
            return Boolean.class;
        }
        return super.getColumnClass(column);
    }

    @Override
    public int getColumnCount() {
        return header.length;
    }

    @Override
    public String getColumnName(int column) {
        return header[column];
    }

    @Override
    public int getRowCount() {
        return data.length;
    }

    @Override
    public Object getValueAt(int row, int column) {
        return data[row][column];
    }

    @Override
    public boolean isCellEditable(int row, int column) {
        return column == 0;
    }

    @Override
    public void setValueAt(Object value, int row, int column) {
        data[row][column] = value;
    }

}

With this short code the table freeze if you change the category. At my whole code,t it freeze too, but I am able to see the updated Table in the background when I resize the window (table resize to the same size as the frame). I don't know why it isn't at the snipped.

EDIT: The problem to change the content has been solved. The source has been updated. But the problem to get the right table size hasent been solved yet. In the source first I use a 3 row array and after that a 2 row array. I want to delete the old table and create a new one, so the row size is right.

Basically I just want to update the table with new content.

  • Thank you for Help!
Gerret
  • 2,948
  • 4
  • 18
  • 28
  • I can't see class Table, code couldn't be compilable table = new JTable(new Table(data, header)); – mKorbel Aug 09 '13 at 11:43
  • Woops I sorry for that wait a moment – Gerret Aug 09 '13 at 11:44
  • in addition to the answer: your model.setValueAt is incorrectly implemented, it _must_ notify its listeners about the change! – kleopatra Aug 12 '13 at 09:34
  • In my original source I do that thats only a lightwight source so I am able to explain it easier! ^^ – Gerret Aug 12 '13 at 09:37
  • **never-ever** show code that's basically incorrect, it'll confuse others. Just leave it out if it's not relevant to the problem at hand :-) – kleopatra Aug 12 '13 at 11:28
  • basically incorrect is hard xD Its just a code that is short and excutable(dont know the short word) and I wrote that quick I know ive to notify but it worked without it ;) but ok Ill do that!! ^^ – Gerret Aug 12 '13 at 11:32

2 Answers2

1

The code is buggy because every time actionPerformed() is called, you are creating a new component:

table = new JTable(new TestTableModel(data, header));
frame.add(new JScrollPane( table ));  // <-- BTW: here you need to put the table otherwise you are adding an empty JScrollPane
frame.validate();

(Note: There is an additional bug, @mKorbel mentioned it).

However you have already added a JScrollPane with a JTable to the frame and those continue to exist. (if you try to resize the window you will see the new table beneath the old one).

The correct way to update the table data is to get its TableModel make any modifications required in the model and then depending on what you changed you will fire the apporpriate fireXXX() method to notify the table to redraw itself.

As a crude example your code would be:

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource() == this.combobox) {
        JComboBox<String> combobox = this.combobox;

        newdata.clear();    // Clear your list or create a new one otherwise data will keep piling up.  
        newdata.add("Test1");
        newdata.add("Test2");

        TestTableModel model = (TestTableModel) table.getModel();

        // Since you need to replace the whole data create a new Object[][] each time
        Object[][] newModelData = new Object[newdata.size()][3];

        // Set whatever data to the new array
        int i = 0;
        for (String text : newdata) {
            newModelData[i][0] = Boolean.TRUE;
            newModelData[i][1] = text;
            newModelData[i][2] = text;
            i++;
        }

        // replace all data of the current model
        model.setData(newModelData);
    }
}
....

// Now inside your table model:
    ...
    @Override
    public Class<?> getColumnClass(int column) {
    // if (column == 0) {   
    //     return Boolean.class;             // <-- this produces a ClassCastException with the data you aretrying to set
    // }
       return super.getColumnClass(column);
    }

    public void setData(Object[][] data) {
        this.data = data;       //  <-- Update the data
        fireTableDataChanged(); //  <-- fire the event so the table is notified. If you change only one cell you need to call the appropriate fire event
    }
    ...

Update 1: Your problem with your new code has fixed the way that you update the data in the model. However you have a logical flaw when you update the data structure. This variable starts as an array of 3 rows. In the actionPerformed() method you perform a loop for the length of newdata list which only has 2 entries. So you update only the first 2 rows of your model.

Update 2: It seems that you are missing the point. How you update your model matters here. The table will display whatever data your model has. If you only update 2 rows but leave the 3rd one unmodified then the table will display 3 rows (2 new ones and an old one). Since you need to change each time all the data then you need to completely replace the data in the model. It is your data that need to be re-created each time not the table. See the update code example. I have added the actionPerformed() method that re-initializes the data with your current source code. Please read the inline comments.

c.s.
  • 4,786
  • 18
  • 32
  • I am really sorry for the messed up code, I mixed somthing new with my original code and got mistakes. Thank you for your help, but here I have the same problem like the code from mKorbel. I have change the code now with your suggestion and the one from mKorbel but as i said if I had 4 rows and get only 2 now from the database it only changes 2 the other 2 are the old data. Pls try again to run the code and change the category so you will see what I mean! – Gerret Aug 09 '13 at 12:46
  • this logic most be code by yourself, there isn't universal notifiers, there are two way 1. notifiers on Database side, which returns only changes with proper coordinates, or 2. create this notifiers on Java side, loop inside ResultSet and by coparing if equals with value stored in XxxTableModel – mKorbel Aug 09 '13 at 12:52
  • Why I need a notifier? Actully I want a new Table with my new Data and than I have to remove the old one. Have you tryed the new code? I used your suggestion, it is working fine but theres only the problem that there is the old row with the old data. Basically I need a new JTable with the right number of rows(col everytime the same) – Gerret Aug 09 '13 at 12:55
  • come on im desperate. Dont you understand my problem I dont know how I am able to explain it I said it three times now. Sure there is only a update of 2 rows. That should show that I want a new Table with only 2 rows. So what ive to do to delete the last. I think that the best way is to delete the whole table and create a new one but i dont know how it works. In the first source i uploded i tryed to delete the table and add a new one to the panel but that dont work thats the reason im here. mKorbel and you told me to change it with a method in my table model ive done that it cool but not all! – Gerret Aug 12 '13 at 05:40
  • @Gerret it seems that you are missing the point here. Please check second update in my answer. I have also added the code of the `actionPerformed()`. You need to re-create your model's data each time, not a table – c.s. Aug 12 '13 at 07:05
  • Yea ^^ that was that what I want to know now it works :) thank you very much – Gerret Aug 12 '13 at 07:25
-1

Try below code after you change the data in jtable.

model.fireTableDataChanged(); table.repaint();

where model is the tablemodel object and table is JTable object

I hope it helps!!!

Sanket Pipariya
  • 79
  • 1
  • 12
  • well with that I cant work it don't answer my question. Because I asked how I actully can change the data. You tell me how i notify listeners and so on. But thank you anyway. – Gerret Aug 09 '13 at 12:16
  • 1
    -1: a well-behaved model _must_ notify its listeners on changes. If manual notification on behalf of the model or manual repaint of the view appears to solve the problem, either the model is misbehaving or used incorrectly. – kleopatra Aug 12 '13 at 09:28