I have question that how can I delete all datas from my jTable in GUI when a user entered a key?
thanks
You can set a new empty data model:
TableModel newModel = new DefaultTableModel();
jtable.setModel(newModel);
You need to understand that a JTable is a view of the data, while the actual data resides in the TableModel. If you need to clear out the table, then you need to clear out the TableModel.
If your TableModel is an AbstractTableModel, you must provide implementations of 3 methods:
public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);
Frequently the actual data objects are stored in an additional data structure (e.g. a list), and then the AbstractTableModel queries that list.
List<DomainObject> objects = new ArrayList<DomainObject>();
public int getRowCount() { return objects.size(); }
// How many columns you make depends on what features of the objects you're exposing.
public int getColumnCount() { return NUMBER_OF_COLUMNS; }
public Object getValueAt(int row, int column) {
DomainObject object = objects.get(row);
... // pull out the property based on the column they pass in
}
// By exposing this method, you can allow your Controller code to reach into this model
// and delete all the rows.
public void clear() {
objects.clear()
}
What HH is suggesting you do is change the model of your JTable to reference an empty model, which will in effect clear out the table. However, the columns etc. will not be persisted correctly (the new DefaultTableModel has no idea what those column names would be).
After you've researched how the view and model fit together more, take a look at GlazedLists. It allows a very powerful way to create TableModels which provide dynamic views of your data, e.g. by filtering out rows that do not match certain criteria.
To sum up - you're not going to find a method on the JTable to clear out its contents, because that's the job of the TableModel. You need some way of ensuring that the TableModel's backing data structures are cleared out.
If you are using the DefaultTableModel then you can just use:
model.setRowCount(0);
This is better than creating a new DefaultTableModel. Creating a new TableModel causes the TableColumnModel to be recreated, which means all the TableColumns will be resize to default values and recreated in the order in which the columns exist in the model. The user may have changed these properties and shouldn't be forced to do it again.
If you are just deleting certain rows that contain a particulsar value, then you can use the DefaultTableModel.removeRow(...) method. Make sure you start by deleting row from the end of the model and count down to 0.
call removeAll of j_table method at addActionListener
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
j_table.removeAll();
data_model_table.setRowCount(0);
}
});
Related
I have a custom AbstractTableModel
That model stores the data in a HashMap. So for my method for getValueAt(int rowIndex, int columnIndex)
I do
new ArrayList<Object>(data.values()).get(index);
However my data has over 2000 entries, so doing this every single time whenever I have to get the data for my table creates a huge performance hit.
So what solution can you recommend?
Should I try using List to store all my data in instead of HashMap?What is the accepted standard for storing data when using table models?
Thanks to anyone for their suggestion, and I aplogize for what might be a stupid question, but I am not too great when it comes to tables and how to store data in them.
A HashMap doesn't generally make a good fit for a table model because the table needs the ability to access data at an row/col location.
A ArrayList of ArrayLists is a reasonable way to store a table model. This still gives you fast access. Getting to a particular row is a constant time lookup, and then getting the column is also a constant time lookup.
If you don't want the overhead of the lists, you can always store the data in a 2D array.
Yes, the code you sight is going to suck in performance terms - for every cell you render, you're creating a new ArrayList based on the values in your Map (you can do the math).
At the very least, do the list creation once, probably in the constructor of your table model, like this (which assumes you've got some arbitary object, that you don't mention in your question, as the values of the map):
public class MyTableModel extends AbstractTableModel
{
private static final int COLUMN_0 = 0;
private static final int COLUMN_1 = 1;
private List<MyObject> data;
public MyTableModel(Map<?, MyObject> data)
{
this.data = new ArrayList<MyObject>(data.values());
}
public Object getValueAt(int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case COLUMN_0: return this.data.get(rowIndex).getColumn0();
case COLUMN_1: return this.data.get(rowIndex).getColumn1();
...
case COLUMN_N: return this.data.get(rowIndex).getColumnN();
}
throw new IllegalStateException("Unhandled column index: " + columnIndex);
}
}
at the moment I have an app that allows me to display data in a Jtable and then when I double click the Jtable this open a little window to edit only 3 fields, comments, expiration date and description. I update the this values (whit a preparedStatement) the thing is that everytime that I make an update to the database my table just refresh itself, changing the dateFormat with the new value that I've just inserted in my other window but with a different format!. How is this possible?
I' don't understand this because the only I only set a model to the table when I press my "search" button which contains the following code:
ArrayList<FiltrosResumen> filtrosResumenList = MainFrame.dataBase.searchFiltroResumen(query);
FiltrosResumenTableModel resumenModel = new FiltrosResumenTableModel(filtrosResumenList);
this.resumenTable.setModel(resumenModel);
hideColumns(1);
I'm using a custom table model containing all the table Fields, so first as you can see I colect all the rows from the database into a ArrayList from a custom object "FiltrosResumen", then I pass this to the constructor from my customTable model "FiltrosResumenTableModel" which extends AbstractTableMode I'm not using any special renders the most important methods are
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return this.filtrosResumen.get(rowIndex).getIdFiltro();
//....
//case 9:
default:
return null;
}
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
FiltrosResumen filtroResumen = new FiltrosResumen();
switch (columnIndex) {
case 0:
filtroResumen = this.filtrosResumen.get(rowIndex);
filtroResumen.setIdFiltro(Long.parseLong(aValue.toString()));
this.fireTableCellUpdated(rowIndex, columnIndex);
break;}
//....
//case 9:
}
And the constructor
public FiltrosResumenTableModel(List<FiltrosResumen> filtrosResumen) {
this.filtrosResumen = filtrosResumen;
}
And as I stated before, the database does not interact directly whit the table since storing the query result in a ArrayList, and then sending this to the constructor of my customTableModel.
EDIT: In order to change the value from one of the rows items I send a FiltrosResumen Object in this way:
FiltrosResumenTableModel modelo = (FiltrosResumenTableModel) this.resumenTable.getModel();
resumen = modelo.getResumen(row);
EditResumenIF editConexionesIF = new EditResumenIF(resumen);
EDIT: Passing a the resumen object to a InternalFrame Constructor (EditResumenIF).So in this new InternalFrame (EditResumenIF) I assign the values to a JCalendar and a JTextField to change the values and then save them. Afther the same object received by the constructor to a method that does the query and then return a string, ( if the string it's empty it' means that the query was successful without any mistakes)
String error = MainFrame.dataBase.updateResumen(resumen, resumen.getIdFiltro());
How comes that my Table knows that the value changed?
The default renderer for a cell of type Object.class is "a label that displays the object's string value." Unless your implementation of TableModel override's getColumnClass() to return some other value, your result is not unexpected. You might compare this example using DefaultTableModel to your implementation.
Addendum: How does my table know that the value changed?
JTable is a TableModelListener; any change to the model is (or should be) propagated to the table. Absent a complete example, I'm guessing that you are using a second table, table2, to edit a copy of certain data obtained from the original, table1.
Verify that you are copying the data in getResumen() and not just copying a reference to the table1 model.
In your implementation of setValueAt() in the TableModel of table2, update the model of table1. The exact mechanism depends on your TableModel; two approaches are contrasted here.
Addendum: I'm not using another tableā¦I'm passing a reference to my internal frame.
The same principles would apply. As an alternative to directly coupling the models, let the table's model be a PropertyChangeListener to the internal frame, as shown here.
I am doing an application in Java using Swing. I have two tables and I have to copy contents from one table to another (Replication.) The problem is if I clear the destination Table rows then my source table rows are also getting deleted.
If I press CopyAll then I will copy all the contents from Table-A to Table-B. If I press clear then I have to clear Table-B. But the problem is Table-A is also getting cleared.
For copying
public void copyAll() {
TableModel tableAModel = tableA.getModel();
tableB.setModel(tableAModel);
repaint();
}
For clearing rows (I am doing for table-B)
public void clearTableB() {
DefaultTableModel clearTableData = (DefaultTableModel) tableB.getModel();
clearTableData.setNumRows(0);
}
I think I am getting problem while copying in copyAll() method. I am getting tableA's Model and then clearing it at clearTable() method.
If the above copyAll() method is wrong please tell me how can I implement copyAll(), removeTableB().
You have copied the TableModel between the two tables. This means the two tables share the same data. If you delete the contents of the TableModel, both tables will loose their data.
You should create two separate TableModel instances, and keep them in sync (for example by using a listener as the TableModel fires events each time the model is updated)
In your copy version, you set the model of the first table to the second table. So the two tables share the same model. You should make a copy of the model :
public void copyAll() {
final TableModel tableAModel = tableA.getModel();
final DefaultTableModel copy = new DefaultTableModel(tableAModel.getRowCount(), 0);
for (int column = 0; column < tableAModel.getColumnCount(); column++) {
copy.addColumn(tableAModel.getColumnName(column));
for (int row = 0; row < tableAModel.getRowCount(); row++)
copy.setValueAt(tableAModel.getValueAt(row, column), row, column);
}
tableB.setModel(copy);
}
Both tables are using the same model. You have to give Table B it's own Model, copy the values manually. Your current copyAll method copies the reference to the Table Model, it doesn't copy the contents.
That is because you shared the TableModel for the two tables. In the copy method, you should create a clone of the Model and use the clone for the second table.
If you are using DefaultTableModel You can get Vector of data from the model using getDataVector() and clone() it.
public void copyAll() {
TableModel tableAModel = tableA.getModel(), tableModelB;
Vector tableModelBDataVector = ((DefaultTableModel)tableAModel).getDataVector();
int tableModelAColumnCount = tableAModel.getColumnCount();
Vector<String> tableModelAColumnVector = new Vector<String>(tableModelAColumnCount);
for (int i = 0; i < tableModelAColumnCount; i++)
tableModelAColumnVector.add(tableAModel.getColumnName(i));
tableModelB = new DefaultTableModel((Vector)tableModelBDataVector.clone(), (Vector)tableModelAColumnVector.clone());
tableB.setModel(tableModelB);
}
In Java I'm using the DefaultTableModel to dynamically add a column to a JTable.
//create DefaultTableModel with columns and no rows
DefaultTableModel tableModel = new DefaultTableModel(columnNames, 0);
JTable table = new JTable(tableModel);
The columnNames variable is a string array with the column names. So after the program is up and running the user has the option to add additional columns. I do so as follows
tableModel.addColumn("New column name");
Which dynamically adds the column to the table as desired. The user can also remove columns added. For this I use the following code:
TableColumn tcol = table.getColumnModel().getColumn(0);
table.getColumnModel().removeColumn(tcol);
which should remove the column at a specified index, I've also tried:
table.removeColumn(sheet.getColumn(assessmentName));
Both of them work (visually), but here's the problem. After deleting an added column, if another column is added and the table refreshes, the previously deleted column is there again. So while it is removing the column visually, neither of the last two code snippets actually removes it from the model. I'm assuming here that since the column was added to the model that is where it needs to be removed from? Is there a specific method that I need to call or some logic that I need to implement to remove the column?
For your table, try calling table.setAutoCreateColumnsFromModel(false);
This post has a good example as to how to delete column and the underlying data.
I'm assuming here that since the column was added to the model that is where it needs to be removed from?
Yes.
Is there a specific method that I need to call or some logic that I need to implement to remove the column?
No, but you can make up your own method:
moveColumn(...); // to move the column to the end
setColumnCount(...); // to remove the last column
As a side note if you want to give the users the ability to hide/show columns check out the Table Column Manager.
Acting at the TableColumn level, as you show, has only a visual impact but no impact on the TableModel whatsoever.
If you want to really remove a column from DefaultTableModel then you'll need to subclass it and then, in your subclass:
public class MyTableModel extends DefaultTableModel {
public void removeColumn(int column) {
columnIdentifiers.remove(column);
for (Object row: dataVector) {
((Vector) row).remove(column);
}
fireTableStructureChanged();
}
}
I haven't checked it, but it should work in your case.
Of course, removeColumn() should be called only from the EDT.
Note that I wouldn't encourage anyone to produce this kind of code; in particular, using, or deriving from, DefaultTableModel is not the best solution to define a TableModel.
The DefaultDataModel doesn't have a really removeColumn() function, so I wrote a function myself, which can actually solve the problem.
private void removeColumn(int index, JTable myTable){
int nRow= myTable.getRowCount();
int nCol= myTable.getColumnCount()-1;
Object[][] cells= new Object[nRow][nCol];
String[] names= new String[nCol];
for(int j=0; j<nCol; j++){
if(j<index){
names[j]= myTable.getColumnName(j);
for(int i=0; i<nRow; i++){
cells[i][j]= myTable.getValueAt(i, j);
}
}else{
names[j]= myTable.getColumnName(j+1);
for(int i=0; i<nRow; i++){
cells[i][j]= myTable.getValueAt(i, j+1);
}
}
}
DefaultTableModel newModel= new DefaultTableModel(cells, names);
myTable.setModel(newModel);
}
We're seeing JTable selection get cleared when we do a fireTableDataChanged() or fireTableRowsUpdated() from the TableModel.
Is this expected, or are we doing something wrong? I didn't see any property on the JTable (or other related classes) about clearing/preserving selection on model updates.
If this is default behavior, is there a good way to prevent this? Maybe some way to "lock" the selection before the update and unlock after?
The developer has been experimenting with saving the selection before the update and re-applying it. It's a little slow.
This is Java 1.4.2 on Windows XP, if that matters. We're limited to that version based on some vendor code we use.
You need to preserve the selection and then re-apply it.
First of all you will need to get a list of all the selected cells.
Then when you re-load the JTable with the new data you need to programmatically re-apply those same selections.
The other point I want to make is, if the number or rows or columns in your table are increasing or decreasing after each table model reload, then please don't bother preserving the selection.
The user could have selected row 2 column 1 having a value say "Duck", before model updation. But after model updation that same data can now occur in row 4 column 1, and your original cell row 2 column 1 could have new data such as "Pig". Now if you forcibly set the selection to what it was before the model updation, this may not be what the user wanted.
So programmatically selecting cells could be a double edged sword. Don't do it, if you are not sure.
You can automatically preserve a table's selection if the STRUCTURE of that table hasn't changed (i.e. if you haven't add/removed any columns/rows) as follows.
If you've written your own implementation of TableModel, you can simply override the fireTableDataChanged() method:
#Override
public void fireTableDataChanged() {
fireTableChanged(new TableModelEvent(this, //tableModel
0, //firstRow
getRowCount() - 1, //lastRow
TableModelEvent.ALL_COLUMNS, //column
TableModelEvent.UPDATE)); //changeType
}
and this should ensure that your selection is maintained provided that only the data and not the structure of the table has changed. The only difference between this, and what would be called if this method weren't overridden is that getRowCount() - 1 is passed for the lastRow argument instead of Integer.MAX_VALUE, the latter of which acts a signifier that not only has all the data in the table changed but that the number of rows may have as well.
I had the same issue in an application. In my case the model in the table was a list of objects, where the object properties where mapped to columns. In that case, when the list was modified, I retrieved the selected index and stored the object that was selected before updating the list. After the list is modified and before the table is updated, I would calculate the position of the selected object. If it was still present after the modification, then I would set the selection to the new index.
Just setting the selected index in the table after the modification will not work, because the object may change position in the list.
As a side note, I found that working with GlazedLists makes life much easier when dealing with tables.
This is default behavior. If you call fireTableDataChanged() the entire table is rebuild from scratch as you set entirely new model. In this case the selection is, naturally, lost. If you call fireTableRowsUpdated() the selection is also cleared in general cases. The only way is to remember selection and then set this. Unfortunately there is no guarantee that the selection will be still valid. Be careful if restoring selection.
for reference, as #Swapnonil Mukherjee stated, this did the trick with a table with selectable rows:
// preserve selection calling fireTableDataChanged()
final int[] sel = table.getSelectedRows();
fireTableDataChanged();
for (int i=0; i<sel.length; i++)
table.getSelectionModel().addSelectionInterval(sel[i], sel[i]);
If I recall correctly, saving selection and re-applying it is what we have done too...
I was facing same issue and when tried to search the reason I got this question but it seems a bug in Java SDK. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4276786
WORK AROUND
A temporary work-around is available. It should be removed once this bug is fixed as it's suitability has NOT been tested against fixed releases.
Use this subclass of JTable.
Note: This is for the MetalLookAndFeel. If using other look and feels, the inner FixedTableUI subclass will have to extend the TableUI subclass for that look and feel.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
public class FixedTable extends JTable {
private boolean isControlDownInDrag;
public FixedTable(TableModel model) {
super(model);
setUI(new FixedTableUI());
}
private class FixedTableUI extends BasicTableUI {
private MouseInputHandler handler = new MouseInputHandler() {
public void mouseDragged(MouseEvent e) {
if (e.isControlDown()) {
isControlDownInDrag = true;
}
super.mouseDragged(e);
}
public void mousePressed(MouseEvent e) {
isControlDownInDrag = false;
super.mousePressed(e);
}
public void mouseReleased(MouseEvent e) {
isControlDownInDrag = false;
super.mouseReleased(e);
}
};
protected MouseInputListener createMouseInputListener() {
return handler;
}
}
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
if (isControlDownInDrag) {
ListSelectionModel rsm = getSelectionModel();
ListSelectionModel csm = getColumnModel().getSelectionModel();
int anchorRow = rsm.getAnchorSelectionIndex();
int anchorCol = csm.getAnchorSelectionIndex();
boolean anchorSelected = isCellSelected(anchorRow, anchorCol);
if (anchorSelected) {
rsm.addSelectionInterval(anchorRow, rowIndex);
csm.addSelectionInterval(anchorCol, columnIndex);
} else {
rsm.removeSelectionInterval(anchorRow, rowIndex);
csm.removeSelectionInterval(anchorCol, columnIndex);
}
if (getAutoscrolls()) {
Rectangle cellRect = getCellRect(rowIndex, columnIndex, false);
if (cellRect != null) {
scrollRectToVisible(cellRect);
}
}
} else {
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}
}
}
Note Curtsey to http://bugs.sun.com