For some reason, nothing changes about this JTable when this is called (this method updates the JTable after a user submits an SQL query).
Givens:
The dataVector and columnNamesVector are verified to be populated correctly.
JTable is a private class variable.
private void updateData() {
updateDataVariables();
table = new JTable(dataVector, columnNamesVector)
{
#SuppressWarnings({ "unchecked", "rawtypes" })
public Class getColumnClass(int column)
{
for (int row = 0; row < getRowCount(); row++)
{
Object o = getValueAt(row, column);
if (o != null)
{
return o.getClass();
}
}
return Object.class;
}
};
}
Any ideas?
It's a common beginner's fallacy to confuse objects with reference variables, but you need to understand that they are quite distinct. When you call this:
table = new JTable(dataVector, columnNamesVector) {.....
You are creating a new JTable object and having the table variable refer to it, but this has no effect on the JTable object that is displayed by the GUI, the one that the table variable was referring to previously. So you're changing the property of the reference variable, but leaving the original object unchanged.
The solution: You should not be creating a new JTable but rather you should be creating a new TableModel and then place that TableModel into the existing and visualized JTable. You can change a table's model by calling setModel(newModel) on it.
Edit: or as wolfcastle noted you could update the existing TableModel rather than create one anew.
You need to tell JTable that the data in model was updated with firing appropriate event.
See this tutorial
Related
I am trying to refresh a JTable using the DefaultTableModel without accessing the table itself again, but only the existing, but then updated table model.
Yet, I tried to update the table model itself and then notify the model about it (see in the code). For some reason, the table will not update. I do not know, if this is an access problem or if it is just not possible.
//in the Gui_Main class
private static void addTables(){
JTable tblMain = new JTable(Util_Tables.dtm);
}
//in the Util_Tables class, if the tables needs to be updated
public static DefaultTableModel dtm;
public static void updateTable(){
dtm = new DefaultTableModel(data, columns);
dtm.fireTableDataChanged();
}
So you're basic structure is all over the place. When you create a new instance of DefaultTableModel and assign it to dtm, this won't be reflected by the JTable, as it is still using the instance it first grabbed when it was created.
Exposing dtm the way you have, opens it up to undesirable modification and voids one of the principles of OO - encapsulation, where the class is responsible for the management of its properties. This is also a reason to reconsider the use of static
A better start would be to create a getter which returns a single instance of DefaultTableModel, so each call to it is guaranteed to return the same instance of DefaultTableModel and stops any one else from changing the underlying reference
private static void addTables(){
JTable tblMain = new JTable(Util_Tables.getModel());
}
//in the Util_Tables class, if the tables needs to be updated
private DefaultTableModel model;
public static DefaultTableModel getModel() {
if (model == null) {
model = new DefaultTableModel();
}
}
Okay, so how about updating the model? Well, you need to start by modifying your updateTable method, so it can be used to actually update the model in some meaningful way
public static void updateTable(Object[][] data, Object[] columnIdentifiers){
model.setDataVector(data, columnIdentifiers);
}
The model will then generate the events it needs itself. If you find yourself calling the fireXxx methods yourself, then it's a good indication that you're doing something wrong.
Let's say I have a List of Items(my own class that I created that have 5 fields). I want to somehow inject these items into the JTable.
And in the JTable I want to have some kind of method like public String determineColumnText(Object o, int col) where I can convert the received Object into Item and then based on the col take out a specific value from the Item and return it so that it will be shown.
I have tried searching and saw numerous answers saying create AbstractTableModel however all the tutorials I looked at do not provide anything that I desired. Closest I saw was public Object getValueAt(int rowIndex, int columnIndex) however that would mean that I would have to store all the objects I want to display inside the AbstractTableModel. But what if I want to make it so that the Object is not stored inside AbstractTableModel but at the same time can be send into the AbstractTableModel.
Any suggestion as to how to go about doing this?
But what if I want to make it so that the Object is not stored inside AbstractTableModel
The Object should always be stored inside the TableModel. You can create the Object externally but then you need to add the Object to the TableModel. Once the Object is part of the model you should only manipulate it through the TableModel methods.
See the Row Table Model for a TableModel that will allow you to store Objects in the model but allow you to retrieve the data as a complete Object if you desire.
You will need to extend the class to implement your own getValueAt() and setValueAt() methods. These methods will access individual properties of your Object. The JButtonTableModel.java example shows how you can do this.
Option 1:
Loop through your list of items.
Build arrays of the data for all cells
Pass that array to a DefaultTableModel's constructor, which you will use in a JTable
Option 2:
Extend DefaultTableModel and override getValueAt(), like you describe
Store a reference to the list in your table model
Option 1 can be simple if the data is not changing. Option 2 is the most flexible but you seem to have an object to it that isn't explained.
I will paste you how I've used AbstractTableModel to populate table.
public class InventoryModel extends AbstractTableModel {
private static final String[] columnNames = { "Owner", "Location", "Sample Name", "Form Factor" };
private List<Samples> samplesList;
public InventoryModel(){
samplesList = SampleQueries.getAvailableSamples();
}
#Override
public int getColumnCount() {
// TODO Auto-generated method stub
return columnNames.length;
}
#Override
public int getRowCount() {
return samplesList.size();
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return samplesList.get(rowIndex).getCurrentOwner();
case 1:
return samplesList.get(rowIndex).getSampleLocation();
case 2:
return samplesList.get(rowIndex).getSampleName();
case 3:
return samplesList.get(rowIndex).getFormFactor();
}
return null;
}
And this is how I call it from the Panel.
table = new JTable();
model = new InventoryModel();
model.fireTableDataChanged();
panel.setLayout(new BorderLayout(0, 0));
table.setModel(model);
JScrollPane scroll = new JScrollPane(table);
panel.add(scroll);
Hope it will work for you
Please help. I have two cells from a jtable, one ID and one Description. The ID and Description are both custom combobox. What I am trying to do is when the ID loses its focus or changes its value, the Description will update based on the value on the ID. How do I do that?
Here is my code for the implementation of both cells:
TableColumn subAccountCol = jTable1.getColumnModel().getColumn(table.findColumn("SubAccount"));
javax.swing.JComboBox accountCbx = new javax.swing.JComboBox(Account.toArray());
javax.swing.JComboBox accountDescCbx = new javax.swing.JComboBox(AccountDesc.toArray());
CompleteText.enable(accountCbx);
CompleteText.enable(accountDescCbx);
jTable1.getColumnModel().getColumn(table.findColumn("Account")).setCellEditor(new ComboBoxCellEditor(accountCbx));
jTable1.getColumnModel().getColumn(table.findColumn("Account Description")).setCellEditor(new ComboBoxCellEditor(accountDescCbx));
The cell editor will ultmately call the method setValueAt() on your table model. In this table model, simply update the linked cell value in addition to the edited celle value, and fire the appropriate change event for both cells.
public MyTableModel extends AbstractTableModel() {
// ...
// modifies the value for the given cell
#Override
public void setValueAt(Object value, int row, int column) {
Foo foo = this.list.get(row);
if (column == INDEX_OF_ID_COLUMN) {
foo.setId(value); // change the ID
fireTableCellUpdated(row, column); // signal the the ID has changed
// and now also change the description
String newDescription = createNewDescription(value);
foo.setDescription(newDescription);
fireTableCellUpdated(row, INDEX_OF_DESCRIPTION_COLUMN); // signal the the description has changed
}
// ...
}
}
Hi Im doing a project for college and have a JTable in my GUI. I want the user to be able to select rows without being able to change the data in the table. I am using two arrays to crate the table not the table model. Thanks
I dont want to use tablemodels!!!!
You can implement the isEditable() method in your table model and return false and your table will not be editable:
public class MyTableModel extends AbstractTableModel{
public void isEditable(){
return false;
}
}
Then you have a table and you set its model to a MyTableModel object eg.
JTable table = new JTable();
table.setModel(new MyTableModel());
Extra Information:
AbstractTableModel.isCellEditable(int,int) returns false by default, so you don't need to override it to get that behavior.
The DefaultTableModel implementation of that method returns true by default, so that one must be overriden if you wish to make cells un-editable.
Resource of this answer.
You should define your own table model, by extending AbstractTableModel, or DefaultTableModel. Just override isCellEditable(int row, int col) and make it return false.
You will need to override the table model's isCellEditable() method to always return false.
I have a JTable which uses a custom TableModel to display a series of object instances. There's a switch case inside getValueAt(int row, int column) to return values according to given attributes (see below). One return statement involves returning a value of 1/0 as true/false.
Is there a way that I can modify this TableModel so that it displays a 1/0 when a cell is edited?
public Object getValueAt(int row, int column) {
User user = (User)dataVector.get(row);
switch (column) {
case ID_INDEX:
return user.getId();
case USERNAME_INDEX:
return user.getUserName();
case PASSWORD_INDEX:
return "****";
case ACTIVATED_INDEX:
return (user.getActivated())?"true":"false";
default:
return new Object();
}
}
The default renderer and editor for Boolean is a JCheckBox. Consider using
case ACTIVATED_INDEX:
return Boolean.valueOf(user.getActivated());
Alternatively,
case ACTIVATED_INDEX:
return (user.getActivated())?"1":"0";
Addendum: As an example, DefaultTableModel does not override getColumnClass(), and AbstractTableModel simply returns Object.class. Your TableModel should override getColumnClass() accordingly:
DefaultTableModel dtm = new DefaultTableModel() {
#Override
public Class<?> getColumnClass(int col) {
return getValueAt(0, col).getClass();
}
};
// add some data
JTable table = new JTable(dtm);
You need to have a look at TableCellRenderer and TableCellEditor:
A TableCellRenderer is responsible for rendering cell data when it is not being edited, where as a TableCellEditor is responsible for providing a component used to edit the value of a cell. You can therefore represent the data in two separate ways depending on whether it is being edited or just rendered as per normal.
You should however consider that if you return a Boolean type from the getValueAt() method, your JTable should automatically render a JCheckBox, when the cell is in edit mode, the JCheckBox value can be changed by clicking on it as usual. To do this just return:
case ACTIVATED_INDEX:
return Boolean.valueOf(user.getActivated());