I have to insert new products in a JTable such that after pressing the "Add product" button, the table should update itself the new data provided. All the products are written into a file and read from it when printing out the table. The problem is that my table just stays the same and only after I exit the GUI application and enter again I can see the new updated JTable.
Here's the relevant code from the GUI class:
private class ManageProductsListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
...
addProductBtn.addActionListener(new AddProductListener());
removeProductBtn.addActionListener(new RemoveProductListener());
manageProductsPanel.add(warehouse);
manageProductsPanel.setVisible(true);
finalPanel.add(manageProductsPanel, BorderLayout.CENTER);
}
}
private class AddProductListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
...
manageProductsPanel.remove(warehouse);
Product product = new Product(insertProductName.getText(), Integer.parseInt(insertPrice.getText()), Integer.parseInt(insertStock.getText()));
warehouse.addProduct(product);
warehouse.writeFile();
warehouse.printProducts();
manageProductsPanel.add(warehouse);
...
}
}
And here's a part from the Warehouse class:
public class Warehouse extends JPanel {
...
private TreeSet<Product> products = new TreeSet<Product>();
private JTable productsTable;
private Object[][] data;
private String[] columns = {"Product name", "Price", "In stock", "Understock", "Overstock"};
public Warehouse() {
}
public void addProduct(Product newProduct) {
products.add(newProduct);
}
public void writeFile() {
try {
FileOutputStream fos = new FileOutputStream("products.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Iterator<Product> i = products.iterator();
while (i.hasNext()) {
oos.writeObject(i.next());
}
oos.close();
} catch (IOException i) {
i.printStackTrace();
}
}
public void readFile() {
...
}
public void printProducts() {
data = new Object[products.size()][5];
Iterator<Product> iterator = products.iterator();
int i = 0;
while (iterator.hasNext()) {
Product p = iterator.next();
data[i][0] = p.getProductName();
data[i][1] = p.getPrice();
data[i][2] = p.getStock();
data[i][3] = p.checkUnderstock();
data[i][4] = p.checkOverstock();
i++;
}
createTable();
}
public void createTable() {
productsTable = new JTable(data, columns);
productsTable.setPreferredScrollableViewportSize(new Dimension(600, 200));
productsTable.setFillsViewportHeight(true);
JScrollPane scroll = new JScrollPane(productsTable);
add(scroll);
}
Any help would be appreciated. Thanks in advance!
Cant add a comment, so I have to post it as an answer. Have you tried to revalidate your panel after you add the table?
manageProductsPanel.revalidate()
Another solution maybe painless would be to reset the model of the table after the update:
productsTable.setModel(new DefaultTableModel(data, columns));
products.add(newProduct);
You should not be updating the products TreeSet. This does not update the data in the TableModel so of course the table is not updated. The data should only be contained in the TableModel and all updates to the data should be done directly to the TableModel.
Therefore you need a custom TableModel that can hold Product objects.
Check out the Row Table Model for a generic TableModel that allows you to store custom objects. The JButtonTableModel.java code then shows how you can extend the RowTableModel to support your custom object.
Then when you create a new Product you just use the addRow(...) method of your newly created ProductTableModel.
Edit:
The answer above is the proper solution. You should never keep two copies of the data.
I add another new product another new table appears and so on
That is because your Warehouse class is a JPanel which uses a FlowLayout, so when you keep adding components to it the panel just keep growing.
The solution is to NOT keep creating new components. Then is no need to keep creating a new JTable and JScrollPanse when you add new Products.
Instead, when you create the Warehouse class you create the table and the ScrollPane. The table will be empty.
Then when you add a Product to the product list you can create a new DefaultTablModel. Then you just use the table.setModel(...) method to update the table. Now the table will repaint itself with the new data.
Related
I created a class that is meant to show a JTable populated with data taken from a database with hibernate:
public class FLlistes extends JInternalFrame {
private JTable table;
private DefaultTableModel model;
//some code for more components of the form
String[] columns = {"Id","Data", "Lloc"};
model = new DefaultTableModel(columns, 0) {
#Override
public Class<?> getColumnClass(int columna) {
if (columna == 2)
return LocalDate.class;
return Object.class;
}
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
table = new JTable(model);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(49, 176, 732, 361);
getContentPane().add(scrollPane);
scrollPane.setViewportView(table);
//some code for more components of the form
}
Then I have a class that makes the queries with hibernate. The next method is supposed to collect data from a table and populate the table I created before.
public class AccionsBD {
public static void GetAllLlistes() {
String jql = "select llc from LlistaCompra llc";
EntityManager entityManager = JPAUtil.getEntityManagerFactory().createEntityManager();
TypedQuery<LlistaCompra> q = entityManager.createQuery(jql,LlistaCompra.class);
List<LlistaCompra> llistes = q.getResultList();
FLlistes fl = new FLlistes();
for (LlistaCompra llista: llistes) {
System.out.println(llista.getIdLlista());
System.out.println(llista.getData());
System.out.println(llista.getLloc());
Object[] objFila = new Object[3];
objFila[0] = llista.getIdLlista();
objFila[1] = llista.getData();
objFila[2] = llista.getLloc();
fl.getModel().addRow(objFila);
}
entityManager.close();
}
}
The purpose of the System.out.println inside the loop is only to check that the query works. The query is working fine, I tried debugging and the end of the loop objFila contains all the correct data, but the table in the form never shows anything besides the table header. What am I missing?
Also, for some reason sometimes when I run the app the form shows up, and somtimes it doesn't. It does this without even changing the code. Why does this happen?
Edit: this is my getter:
public DefaultTableModel getModel() {
return model;
}
What I am trying to do is to populate a JTable from an ArrayList.
The array list is a type Record which I have defined below:
public class Record {
int Parameter_ID;
int NDC_Claims;
int NDC_SUM_Claims;
public Record(int parameter, int claims, int ndc_sum_claims){
Parameter_ID = parameter;
NDC_Claims = claims;
NDC_SUM_Claims = ndc_sum_claims;
}
public Record() {
// TODO Auto-generated constructor stub
}
I don't know how to populate the table with the column headers as well. This is what I have so far:
DefaultListModel listmodel = new DefaultListModel();
ArrayList<Record> test = new ArrayList<Record>();
DefaultTableModel modelT = new DefaultTableModel();
Object data1[] = new Object[3];
for(int i=0; i<test.size();i++){
data1[0] = test.get(i).Parameter_ID;
data1[1] = test.get(i).NDC_SUM_Claims;
data1[2] = test.get(i).NDC_Claims;
modelT.addRow(data1);
}
table_1 = new JTable(modelT, columnNames);
contentPane.add(table_1, BorderLayout.CENTER);
contentPane.add(table_1.getTableHeader(), BorderLayout.NORTH);
Nothing is outputted. Any help would be great!
Well you need to start by reading the API. You can't program if you don't read the API first.
DefaultTableModel modelT = new DefaultTableModel();
When you read the API what does that constructor do? It creates a model with 0 rows and 0 columns. You will want to create a model with 3 columns and 0 rows so that you can add rows of data to the model. Read the DefaultTableModel API
table_1 = new JTable(modelT, columnNames);
What does that statment do? I don't see a constructor that allows you to specify a model and column names so how does your code compile. You just want to create the table using
the model.
contentPane.add(table_1, BorderLayout.CENTER);
contentPane.add(table_1.getTableHeader(), BorderLayout.NORTH);
The table should be added to the viewport of a JScrollPane. The header will then be displayed as the column header of the scroll pane.
Read the JTable API. The API also has a link to the Swing tutorial on How to Use Tables you need to read for the basics.
ArrayList<Record> test = new ArrayList<Record>();
You create an empty ArrayList. So what do you expect to happen when you iterate through the loop? How can you add data to the model if there is no data in the ArrayList?
Also, did you search the forum/web for examples that use the DefaultTableModel or JTable classes. Those examples will help you write your code.
You can create a custom AbstractTableModel and then create a JTable using that model.
Here is a class handling ArrayList of Arrays:
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class DataTableModel<T> extends AbstractTableModel {
/**
* Benjamin Rathelot
*/
private static final long serialVersionUID = -7361470013779016219L;
private ArrayList<T[]> data = new ArrayList<T[]>();
private String[] tableHeaders;
public DataTableModel(ArrayList<T[]> data, String[] headers) {
super();
this.data = data;
this.tableHeaders = headers;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
if(data.size()>0) return data.get(0).length;
return 0;
}
#Override
public Object getValueAt(int arg0, int arg1) {
// TODO Auto-generated method stub
if(data.size()>=arg0) {
if(data.get(arg0).length>=arg1) {
return data.get(arg0)[arg1];
}
}
return null;
}
#Override
public String getColumnName(int columnIndex) {
return tableHeaders[columnIndex];
}
}
I have the following code for creating a JTable
public void tableTeam()
{
rl.readRunners();
String[] sampleHeaders = {"Athlete ID", "Team"};
JTable myTable = new JTable(rl.teamTableData,sampleHeaders);
myTable.setAutoCreateRowSorter(true);
myTable.setRowHeight(20);
///////////////
sorter = new TableRowSorter(myTable.getModel());
List sortKeys = new ArrayList();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.DESCENDING));
sorter.setSortKeys(sortKeys);
sorter.setRowFilter
(
new RowFilter<TableModel, Integer>()
{
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
{
boolean included = true;
Object cellValue = entry.getModel().getValueAt(entry.getIdentifier(), 0);
if (cellValue == null || cellValue.toString().trim().isEmpty())
{
included = false;
}
return included;
}
}
);
myTable.setRowSorter(sorter);
///////////////
teamScrollTable = new JScrollPane(myTable);
teamScrollTable.setSize(500,300);
teamScrollTable.setLocation(50,100);
System.out.println("Creating team table");
teamPanel.add(teamScrollTable);
}
And then I have this code to update the table.
public void RefreshTeam()
{
teamPanel.remove(teamScrollTable);
rl.readRunners();
String[] sampleHeaders = {"Athlete ID", "Team"};
JTable myTable = new JTable(rl.teamTableData,sampleHeaders);
myTable.setAutoCreateRowSorter(true);
myTable.setRowHeight(20);
///////////////
sorter = new TableRowSorter(myTable.getModel());
List sortKeys = new ArrayList();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.DESCENDING));
sorter.setSortKeys(sortKeys);
sorter.setRowFilter
(
new RowFilter<TableModel, Integer>()
{
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
{
boolean included = true;
Object cellValue = entry.getModel().getValueAt(entry.getIdentifier(), 0);
if (cellValue == null || cellValue.toString().trim().isEmpty())
{
included = false;
}
return included;
}
}
);
myTable.setRowSorter(sorter);
teamScrollTable = new JScrollPane(myTable);
teamScrollTable.setSize(500,300);
teamScrollTable.setLocation(50,100);
System.out.println("Changing team table");
teamPanel.add(teamScrollTable);
}
This code is activated when a button is clicked on the program. However unlike my expectations this does not update the table. I did some research and found this line of code fireTableCellUpdated(). Although I am unable to implement this into my code so that it updates the table since I do not know how. I would greatly appreciate someone helping me implement this code or showing me a better way to do what I want.
In order to update the table you can use the following:
myTable.setModel(new DefaultTableModel(rl.teamTableData,sampleHeaders));
The refreshTeam method (note java methods should begin with lowercase) is not updating the JTable, it is creating a new table entirely, and then trying to add these Components to (what I presume is) an already visible UI. The UI is unaware of this, so after adding/removing Components you should call...
revalidate();
repaint();
...on the components. There are alternatives to this approach - you can change the model rather than recreating the table. You can do so by implementing your own AbstractTableModel (you can then call the fireTableDataChanged that you refer to on the instance to update listeners that the data has changed).
This question already has an answer here:
Store products in a TreeSet and print the content in a JTable
(1 answer)
Closed 7 years ago.
I'm trying to display a single JTable, but I keep getting many new JTables everytime I insert a new product: http://i.stack.imgur.com/gyNsn.png
How can I display just one JTable and also make the column names visible?
Here is the method that creates the table:
public JTable populate(Product p) {
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
Vector<Object> row = new Vector<Object>();
Vector<String> headers = new Vector<String>();
headers.add("Product name");
headers.add("Price");
headers.add("In stock");
row.add(p.getProductName());
row.add(p.getPrice());
row.add(p.getStock());
data.add(row);
productsTable = new JTable(data, headers);
return (new JTable(data, headers));
}
And here is a part from the GUI class:
addProductBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Product product = new Product(insertProductName.getText(), Integer.parseInt(insertPrice.getText()), Integer.parseInt(insertStock.getText()));
warehouse.addProduct(product); // by using a TreeSet
productsTable = warehouse.populate(product); // here I call the earlier defined method
warehouse.initFile(); // I wrote the productsTable content into a binary file, so that it can act like a database
warehouse.readFile();
warehouse.populate(product);
manageProductsPanel.add(productsTable);
});
The populate method you posted creates a new JTable every time it is called. Given this is called every time the ActionListener is called, a new JTable will be added. You should consider creating your own TableModel - extend AbstractTableModel and override the necessary methods, returning the appropriate values for each row/column. A simple example is below, making some assumptions about project structure for demo's sake (for instance warehouse is an instance of a List):
public class MyTableModel extends AbstractTableModel{
#Override
public int getColumnCount() {
return 3;
}
#Override
public int getRowCount() {
return warehouse.size();
}
#Override
public Object getValueAt(int arg0, int arg1) {
switch(arg1){
case 0:
return warehouse.get(arg0).getName();
case 1:
return warehouse.get(arg0).getPrice();
default:
return warehouse.get(arg0).isInStock();
}
}
#Override
public String getColumnName(int col){
switch(col){
case 0:
return "Name";
case 1:
return "Price";
default:
return "In STock";
}
}
}
You can then create an instance of this class, and set the table model for the JTable. Every time the backed List is updated, you can update the Listeners of the TableModel
MyTableModel tableModel = new MyTableModel();
myTable.setMOdel(tableModel);
.......
//when an item is added to
warehouse.add(item);
tableModel.fireTableDataChanged();
There are more demonstrations for how to customize a JTable in the Oracle Tutorials
I have a tree and a table on my panel, when I click the tree node, the table needs to change at the same time, but it doesn't. I search online and read the Java tutorial and didn't find any solutions. From some posts I think I need to use fireTableStruetureChanged(), but it just doesn't work in my code. Could anyone help me out of this? The following is the code. Thanks a ton!
public class tableStructureChange extends JFrame implements ... {
.....
/ //columnNames is a public variable, because I need to change the columns later
columnNames = new String[] {"col1","col2"}; */
data = new String[][]{
{"Mary", "Campione"},
{"Alison", "Huml"}, };
table = new JTable(new MyTableModel());
table.setAutoCreateColumnsFromModel( false );
feedback = new JScrollPane(table); //feedback is the bottom panel
...
}
//the following class is the problem, i need the table to be reloaded
//when the class is called, but the table doesn't change at all
public void displayFeedback(String tempString) {
//create table for bottom panel
columnNames = new String[] {"col3","col4", "col5"};
String[][] data = new String[][]{
{"Mary", "Campione", "us"},
{"Alison", "Huml", "canada"}, };
//table = new JTable(data, columnNames);
//fireTableStructureChanged(); //this is the problem part
}
// my table model
class MyTableModel extends AbstractTableModel {
String[] columnNames = new String[] {"col1","col2"};
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
}
...
}
In your method displayFeedback you seem to be hoping to replace the JTable object and have the display change to reflect what is selected in the JTree above. Instead of replacing what is in the View object, you should focus your effort on updating the Model, in this case, the AbstractTableModel subclass that you have created. There are a couple ways you can do that, but for a brute force proof of concept, you could do something like the following:
add a constructor to MyTableModel that takes a 2 dimensional array of data
in displayFeedback, create a new instance of MyTableModel that has new data relevant to the tree node that was selected.
call setModel on your global table variable.