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());
Related
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
I have a tablecolumn with custom cell render this cell render takes an object and renders its properties as Labels. The problem is that I can't find a way to pass the same object in the arraylist to a column. Here is my code:
//I want to render this object in a column as well as use it in the rest of columns
CustomerCreationFlow cflow=new CustomerCreationFlow();
cflow.setId(10L);
cflow.setFirstName("Feras");
cflow.setLastName("Odeh");
cflow.setCustomerType("type");
ObservableList<CustomerCreationFlow> data = FXCollections.observableArrayList(cflow);
idclm.setCellValueFactory(new PropertyValueFactory<CustomerCreationFlow, String>("id"));
//I tried this but it didn't work
flowclm.setCellValueFactory(new PropertyValueFactory<CustomerCreationFlow, CustomerCreationFlow>("this"));
typeclm.setCellValueFactory(new PropertyValueFactory<CustomerCreationFlow, String>("customerType"));
flowTable.setItems(data);
Any Suggestion?
You should implement your custom CellFactory by extending TableCell.
In your custom TableCell, you can get the value of the line of the table (logically CustomerCreationFlow) by getting the TableRow of the current TableCell.
That gives:
class MyTableCell<S,T> extends TableCell<S, T>
#Override
public void updateItem(final T item, final boolean empty) {
super.updateItem(item, empty);
if (empty) {
this.setText(null);
this.setGraphic(null);
} else {
S item = (S) this.getTableRow().getItem();
// DO STUFF HERE
}
}
}
T is the type of the data defined by CellValueFactory. S is the type of the data representing a row.
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
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.