Change/Update JTable content with JComboBox(category)

孤者浪人 提交于 2019-12-20 04:51:27

问题


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!

回答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.




回答2:


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!!!



来源:https://stackoverflow.com/questions/18145754/change-update-jtable-content-with-jcomboboxcategory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!