I have created a DefaultTableModel that is showing a very basic user league table in a JTabbedPane.
I want to add a row using some data I collect from a different class. This code will not run but here is a sample:
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class Statistics extends JPanel {
public Object[][] data;
public Statistics() {
super(new GridLayout(1,0));
String[] columnNames = {"Name", "Games Played", "Games Won"};
Object[][] data = {
{"Tom", new Integer(5), new Integer(2)},
{"Steve", new Integer(2), new Integer(0)},
};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
JTable table = new JTable(model);
table.setFillsViewportHeight(true);
table.setVisible(true);
table.setEnabled(false);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
}
then I call this from my main class:
...
stats = new JPanel(); //create a new JPanel for table to go on
...
tp.addTab ("Statistics", stats); // add panel to JTabbedPane
..
leagueTable = new Statistics();// add the table to the stats panel
stats.add(leagueTable);
this is showing up and its fine, but can anyone guide me as to what syntax I use to add a row, I have tried:
leagueTable.addRow(table.getRowCount(), new Object[]{"ange", 5, 3});
but this does not work, eclipse asks me to add a method called 'addRow' to Statistics class, but I thought 'addRow' was already a method of DefaultTableModel, so I am very confused. Can anyone help me out on how to add a row of data to the table? Thanks IA
addRow is a method of the TableModel but you're invoking the method on the Statistics class. You could create a new method to add the data:
public class Statistics extends JPanel {
private DefaultTableModel model
public Statistics() {
super(new GridLayout(1,0));
model = new DefaultTableModel(data, columnNames);
...
}
public void addData(Object[] data) {
model.addRow(data);
}
}
Side note: Typically you don't want to extend JPanel here if not adding new functionality so a redesign is probably in order
leagueTable is an instance of your Statistics class. So it doesn't have an addRow() method.
Possible solution:
Make your DefaultTableModel a variable in your Statistics class.
Create a method in Statistics that interacts with the table model by
calling addRow() on the model.
Related
2015.5.5 22:11 updated. I found that, when I create a sorter and call setRowSorter() method in the MyTable's construction, afterwards, it
will keep the original line number(in which the data still refresh
correctly but not easy to discover)even though the dataModel inside is
already changed many times which can be proven as
printf(getModel().getRowCount()). What's more,
MyTable.getAutoCreateRowSorter() always return true. I must explicitly
call setAutoCreateRowSorter(true) to fix the issue it if I called
setRowSorter(). I am happy but this is still wierd.
[2015.5.5 6:19 updated] I have found a way to access: make a "setRow" with the combination of insertRow() and removeRow() and everytime I
update, I setrRow all the rows in the table. It reflect immediately in
the UI. BUT there will be a series of error begin with "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Invalid index" and I guess it's about some kiind of swing thread problem because I can see "RepaintManager" or "paint" in the error. It occurs especially when I move the scrollbar when it's running.(but it still occur if I don't move it)
I have a JTable in a JScrollPane in a JFrame. I initial a MyTableModel with a data and use it to Create a JTable.Like this
MyTableModel extends DefaultTableModel{
Data data;
//object
MyTableModel (Data a){
data = a;
// do something to initial the table model,like build object[][] and add rows.
}
}
class main{
MyTableModel tm = new MyTableModel(data);
Jtable table = new JTable(tm);
JScrollpane jsp = new JScrollpane(table);
JFrame window = new JFrame();
window.getxxxpane().add(jsp);
}
So, as my data is always changing/updating and the changed row is plural and impossible to caculate.
while(true){
data.change();
refreshing the table to display the data immediately;
}
my idea is to simply build a new MyTableModel object and set it as the table's model like:
table.setModel(new MyTableModel(data)); //data had been changed.
which doesn't work.
and I tried this:
tm = MyTableModel(data);
tm.fireTableDataChanged();
which doesn't work either.
and the combination as well:
MyTableModol nm = new MyTableModel(data); //data had been changed
table.setModel(nm);
nm.fireTableDataChanged();
Could someone please give me some clue to change the TableModel object in an unchangable Jtable and update everytime.I dont want to change the tableModel Object because the calculation is huge, instead ,i Want to always create a new object with the construction method's parameter(changed data).
the most worst method is to remove the JScrollpane and rebuild one table/tablemodel/jscrollpane and re-add it, in which I have to call window.setVisible(true). window.repait() doesn't work,either,unless I move it.
I create a space-wasting but runnable program for demostration ,which most of them are nonsense.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Formatter;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
class TM extends DefaultTableModel {
int[] inside;
static String[] columnNames = { "Hellium", "Radon",
};
TM(int[] data) {
super(columnNames, 0);
this.inside = data;
update();
}
void update() {
Object[][] data = new Object[2][columnNames.length];
for (int i = 0; i < 2; ++i) {
data[i][0] = inside[0];
data[i][1] = inside[1];
// setValueAt(aValue, row, column);
addRow(data[i]);
}
fireTableDataChanged();
}
}
class idkName {
TM tm;
JButton jb, jb2;
int data[] = { 1, 2 };
int data2[] = { 9, 10 };
JTable table;
JScrollPane jsp;
JFrame twindow;
idkName() {
JFrame window = new JFrame();
window.setSize(400, 400);
window.setLayout(new BorderLayout());
jb = new JButton("show");
jb2 = new JButton("change model");
window.add(jb, BorderLayout.EAST);
window.add(jb2, BorderLayout.WEST);
twindow = new JFrame();
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
tm = new TM(data);
table = new JTable(tm);
jsp = new JScrollPane(table);
twindow.getContentPane().add(jsp);
twindow.setSize(500, 500);
twindow.setVisible(true);
}
});
window.setVisible(true);
jb2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// tm = new TM(data2);
tm = new TM(data2);
System.out.println(""+tm.getValueAt(0,0));
tm.fireTableDataChanged();
twindow.setVisible(true);
}
});
}
}
public class main2 {
TM tm;
public static void main(String[] args) {
idkName i = new idkName();
}
}
If you're going to extend DefaultTableModel, then when the data is changed, you need to update it in DefaultTableModel's internal representation using the setValueAt or setDataVector methods. If you have extensive changes (which it sounds like you do), use the setDataVector method to change all the cells and the table structure at once. Your example code looks like it's missing some updates because it's not pushing the new values in to the DefaultTableModel's data vectors.
But since you have a lot of updates, you're probably better off avoiding DefaultTableModel and just extending AbstractTableModel, using your own custom data storage, and calling the appropriate fireXxx() methods whenever your data changes. This will probably be more efficient in terms of both data conversion and number of events raised.
And then make sure all the event and value change work is done on the AWT event thread, using SwingWorkers or other threading support.
Why is there an A on the top of the table?
I placed a JTable inside a JScrollPane to make it scrollable.
Are there methods that I need to place?
I did not place a letter A though so I cant track.
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.BoxLayout;
import javax.swing.JTable;
import javax.swing.border.LineBorder;
import java.awt.Color;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
public class Rawr {
private JFrame frame;
private JScrollPane scrollPane;
private JTable table;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Rawr window = new Rawr();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Rawr() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
scrollPane = new JScrollPane();
scrollPane.setToolTipText("");
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
panel.add(scrollPane);
table = new JTable(100, 1);
table.setBorder(new LineBorder(new Color(0, 0, 0)));
scrollPane.setViewportView(table);
}
}
How do I remove it? Thanks!
Set the column name explicitly using:
String[] colNames = new String[]{"Your Column Name"};
DefaultTableModel defaultTableModel = new DefaultTableModel(colNames, 100);
table = new JTable(defaultTableModel);
If you create a table using new JTable(100, 1) you will see the A, B and so on column headers because the constructor javadoc says:
Constructs a JTable with numRows and numColumns of empty cells using DefaultTableModel.
Since the JTable constructor does not have any information about the column headers. It can only create a DefaultTableModel that does not know any column header names.
The DefaultTableModel extends AbstractTableModel and the javadoc of AbstractTableModel.getColumnName() says
Returns a default name for the column using spreadsheet conventions: A, B, C, ... Z, AA, AB, etc. If column cannot be found, returns an empty string.
Add the following code after the creation of your table.
String[] columns = new String[]{"Column Name"};
((DefaultTableModel)table.getModel()).setColumnIdentifiers(columns);
How do I remove it?
A JTable is designed to display data with a header to describe the data in the column.
Since you only have a single column of data, if you don't want the header then don't use a JTable. Instead you can use a JList.
Read the section from the Swing tutorial on How to Use Lists for more information and examples.
I have noticed that when I have a JTable with a TableRowSorter contained by a JScrollPane, the vertical scrollbar does not appear until after I have created SortKeys for the sorter (which is done by calling toggleSortOrder() for one of the columns).
My question is really why? What do SortKeys have to do with a vertical scrollbar?
Update: Added SSCCE that opens a JFrame with a JTable inside a JScrollPane, that sits in a Container along with a "Populate" button. When the table is initially painted, there is no data and hence no need for a scroll bar. After I populate it with 20 rows, there is a need for a scroll bar, but none appears.
There are two ways to make the scroll bar appear:
Click on either of the header cells to cause a sort to occur.
Remove the commented call toggleSortOrder() in the Container's refresh() method.
// table.getRowSorter().toggleSortOrder(0);
toggleSortOrder() calls setSortKeys() calls sort() calls fireRowSorterChanged() and eventually something catches the event and adds the scroll bar.
package test;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
#SuppressWarnings("serial")
public class TestFrame extends JFrame
{
class MyTableModel extends AbstractTableModel
{
public List<String> names = new ArrayList<String>();
public int getRowCount ()
{
return names.size();
}
public int getColumnCount ()
{
return 2;
}
public String getColumnName (int columnIndex)
{
return columnIndex > 0 ? "Name" : "Number";
}
public Class<?> getColumnClass (int columnIndex)
{
return String.class;
}
public Object getValueAt (int row, int col)
{
return row < names.size() ? col == 0 ? Integer.toString(row) : names.get(row) : "";
}
public void refresh (List<String> names)
{
this.names = names;
}
}
class MyContainer extends java.awt.Container implements ActionListener
{
JTable table;
MyTableModel model = new MyTableModel();
private TableRowSorter<MyTableModel> sorter;
public MyContainer()
{
}
public void init ()
{
sorter = new TableRowSorter<MyTableModel>(model);
table = new JTable(model);
table.setBorder(BorderFactory.createEmptyBorder());
table.setRowHeight(35);
table.getTableHeader().setPreferredSize(new Dimension(200, 35));
table.setRowSorter(sorter);
table.setPreferredScrollableViewportSize(new Dimension(200, 70));
table.setFillsViewportHeight(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBounds(10, 10, 200, 210);
//Add the scroll pane to this panel.
add(scrollPane);
JButton btn = new JButton("Populate");
btn.setActionCommand("populate");
btn.addActionListener(this);
btn.setBounds(10, 220, 200, 35);
add(btn);
}
public void refresh (List<String> rows)
{
model.refresh(rows);
try
{
// Notify sorter that model data (possibly number of rows) has changed.
// Without this call, the sorter assumes the number of rows is the same.
table.getRowSorter().allRowsChanged();
// Do we really want to toggle the sort order every time we refresh?
// User can toggle the sort order himself by clicking on the
// appropriate header cell.
List<?> keys = table.getRowSorter().getSortKeys();
if (null == keys || keys.isEmpty())
{
// table.getRowSorter().toggleSortOrder(0);
}
} catch (Exception e)
{
e.printStackTrace();
}
table.repaint();
}
public void actionPerformed(ActionEvent e)
{
if ("populate".equals(e.getActionCommand()))
{
List<String> rows = new ArrayList<String>();
for (int ii = 0; ii < 20; ii++)
{
rows.add(String.format("%02d", new Integer(ii)));
}
refresh(rows);
}
}
MyTableModel getModel ()
{
return model;
}
}
public static void main (String args[])
{
new TestFrame();
}
MyContainer myContainer = new MyContainer();
TestFrame()
{
myContainer.init();
myContainer.table.getSelectionModel().clearSelection();
add(myContainer);
this.setSize(240, 310);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//pack();
setVisible(true);
}
}
Well, that is not really a SSCCE because you are using a custom TableModel. If you would have created a proper SSCCE you would be using the DefaultTableModel so that you are testing your code with standard JDK classes. If you did this then you would have noticed that the code would work.
So then your next step would be to try the code with your custom TableModel and you would notice that the code did not work.
So then your question on the forum would be why doesn't the code work with my custom TableModel? The point of the SSCCE is to do basic debugging to isolate where the error is happening so we have information to work with. In your original question we had no idea you where using custom classes.
Anyway, the problem is that your custom TableModel is not notifying the table when a change to the data is made. In your refresh(...) method you need to add the following after you reset the List containing the data:
fireTableRowsInserted(0, names.size()-1);
There is no need for table.repaint() in any of your code.
Why won't table cell 0,1 change from aaa to XXXX?
import java.awt.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
class MainFrame {
public static void main(String[] args) {
JFrame f = new JFrame("Refreshing JTable");
JPanel p = new JPanel(new GridBagLayout());
DefaultTableModel productsModel;
JTable productsTable;
f.setSize(800, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] tableTitle = new String[] {"ID", "Name"};
String[][] tableData = {{"1", "AAA"},{"2", "BBB"}};
productsModel = new DefaultTableModel(tableData, tableTitle);
productsTable = new JTable(productsModel) {
public boolean isCellEditable(int r, int c) {
return false;
}
};
JScrollPane scrollpane = new JScrollPane(productsTable);
tableData[0][1] = "XXXX";
f.add(p);
p.add(scrollpane);
f.validate();
f.setVisible(true);
}
}
REASON:
Apparently trying to update the array where data is stored will result in JTable not changing. Either DefaultTableModel needs to be updated or the whole table needs to be redrawn.
EDIT (possible Solution) One way is using Timer:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
class MainFrame {
static JFrame f = new JFrame("Refreshing JTable");
static JPanel p = new JPanel(new GridBagLayout());
static DefaultTableModel productsModel;
static JTable productsTable;
public static void main(String[] args) {
runGui();
}
#SuppressWarnings("serial")
public static void runGui() {
f.setSize(800, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] tableTitle = new String[] {"ID", "Name"};
String[][] tableData = {{"1", "AAA"},{"2", "BBB"}};
productsModel = new DefaultTableModel(tableData, tableTitle);
productsTable = new JTable(productsModel) {
public boolean isCellEditable(int r, int c) {
return false;
}
};
JScrollPane scrollpane = new JScrollPane(productsTable);
tableData[0][1] = "XXXX";
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
addColumns();
remakeData();
productsTable.setModel(productsModel);
}
});
t.start();
f.add(p);
p.add(scrollpane);
f.validate();
f.setVisible(true);
}
private static void addColumns() {
productsModel.setColumnCount(0);
productsModel.addColumn("ID");
productsModel.addColumn("Name");
}
private static void remakeData() {
productsModel.setRowCount(0);
productsModel.insertRow(productsModel.getRowCount(), new Object[] {"1", "Dummy item 1"});
productsModel.insertRow(productsModel.getRowCount(), new Object[] {"2", "Dummy itme 2"});
productsModel.insertRow(productsModel.getRowCount(), new Object[] {"3", "Dummy item 3"});
productsModel.insertRow(productsModel.getRowCount(), new Object[] {"4", "Dummy item 4"});
productsModel.insertRow(productsModel.getRowCount(), new Object[] {"5", "Dummy item 5"});
}
}
EDIT(much better solution, the way it worked for me flawlessly) Using a static method. Since I'm adding new data in array through another Frame, I created a static method in MainFrame, which I call every time I add/update/delete Object in array. This method will redo the whole model after update and will therefore refresh table.
One problem with the SSCCE posted on your related thread is that the code changes the array which originally formed the table model, whereas it should be changing the table model itself.
The solution to your problem (and the previous one) is rather simple, but unfortunately a bit too long to post it in a comment
You make sure you do some reading on how a JTable actually works. A good starting point is the Swing tutorial and perhaps even the class javadoc of the most important classes JTable and TableModel. Another good read about Swing and the use of MVC is this document. It is much broader then only JTables but will provide you with more insight in how all Swing components are designed, and how you should use them.
After reading that tutorial, you should understand that if you want to update the data which is shown in the table, you update the model of your table. The model will fire events which are received by the table, and the table will make sure it updates itself. After all, the table is only a view on the data (=the TableModel) so updates on the take should take place on the model side. If this is not clear, go back to step 1
Now take a look at your code and remove the listener. Instead, update the model on the table and see how the table updates itself.
If this does not work, you come back to this site with an SSCCE which you post. That way we can see that you tried something, what you tried and most likely what is wrong in your code. This SSCCE should include your code of the TableModel you use and the code that updates your TableModel since that is the most likely location for your problem.
I am creating a table at run time after fetching the result from the database. The flow of my application is like this.
Initializing a empty JScrollPane(); JScrollPane tableScroll = new JScrollPane();
Fetching result from the database;
Updating result to JTable and adding JTable to JScrollPane using following method:
Code:
private void setResultTable(Vector documents, Vector header) {
TableModel model = new DefaultTableModel(documents, header);
documentTable.setModel(model);
tableScroll.add(documentTable);
tableScroll.repaint();
}
my problem is that after calling setResultTable the result are not appearing in the table. Please help me with that. Thanks in advance !!!
You appear to be adding the JTable directly to the JScrollPane. If so this isn't correct and you'll want to change this so that you're actually adding the table to the scroll pane's viewport:
tableScroll.getViewport().add(documentTable);
There is no need to repaint the JScrollPane after doing this.
For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class AddTableToScroll {
public static void main(String[] args) {
final JScrollPane sPane = new JScrollPane();
// Sorry Jeanette!!
sPane.setPreferredSize(new Dimension(200, 150));
JButton button = new JButton(new AbstractAction("Press Me!") {
public void actionPerformed(ActionEvent arg0) {
DefaultTableModel model = new DefaultTableModel(new Integer[][] {
{ 1, 2 }, { 3, 4 } }, new String[] { "A", "B" });
JTable table = new JTable(model);
sPane.getViewport().add(table);
}
});
JPanel panel = new JPanel();
panel.add(sPane);
panel.add(button);
JOptionPane.showMessageDialog(null, panel);
}
}
If this doesn't help, you'll want to also tell us exactly what happens, what you see, and if possible supply us with a small compilable runnable program that demonstrates your problem, an SSCCE