I'm desperately trying to convince my JTable to refresh when I change its data. The data are stored in a global singleton, TreeSet, that I'm working with. Whenever the TreeSets data is altered, an event is fired to refresh the TableModel. For test purposes, I replaced this by a simple Timer firing events.
Each time an event is fired, an element from the TreeSet is removed to simulate changes in the data. The event fires and the TableClass receives it and processes it as expected, but when it comes to refreshing nothing happens. I tried to create a new TableModel each time an event occurs and set it to the global table. The changes to the singleton TreeSet are made but nothing happens to the JTable.
public class TreeTableObj implements ActionListener{
public JTable table ;
public TreeTableObj(){
MyTableModel myModel = new MyTableModel(getValuesOfTreeSet(), getTreeSetRows());
table = new JTable( myModel){ //some rendering };
table.setAutoCreateRowSorter(true);
}
class MyTableModel extends AbstractTableModel {
private String[] columnNames;
private Object[][] data ;
public MyTableModel(Object[][] data, String[] columnNames){
this.data = data;
this.columnNames = columnNames;
System.out.println("Model created");
}
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];
}
public void setData(Object[][] data){
this.data = data;
fireTableDataChanged();
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
if (col < 2) {
return false;
} else {
return true;
}
}
public void setValueAt(Object value, int row, int col) {
if (true) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value
+ " (an instance of "
+ value.getClass() + ")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);
if (true) {
System.out.println("New value of data:");
printDebugData();
}
}
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
public void refreshTableModel(){
FlightsTreeSet.getInstance().remove(FlightsTreeSet.getInstance().first());
table.setModel(new MyTableModel(getValuesOfTreeSet(), getTreeSetRows()));
}
public void actionPerformed(ActionEvent arg0) {
refreshTableModel();
}
}
Any help would be appreciated!
[EDIT1]
I am getting closer to the problems Core:
I found out that the table which is displayed in the JFrame holds another reference as the one that I change. I made all changes visible and they apply to the Model.
Is something missing in your code? Your table model as it is shouldn't compile, because it is missing all the methods that JTable would use to access the data (getColumnCount(), getValueAt(), etc.)
Also, you shouldn't have to create a new model on every change - just have the model fireTableStructureChanged().
I found the problem. And leaving the answer here to be found by others...
After realizing that the displayed JTable and the one that I was editing hold different references (see [EDIT1]) I found out that I was creating two different Objects of the JTable. One to be displayed and the other one in main() to become an ActionListener. The Listener received the Events and did the changes but the other one never noticed that anything was happening.
Related
I would like to know how to set up a JComboBox in a particular cell in a JTable.
I have seen people using TableColumn setCellEditor(new DefaultCellEditor(comboBox)).
But this is for an entire column, I would like a specific cell.
So maybe I should do a custom TableCellEditor that would fit my needs, but I am a little lost on how to do it...
The goal of this is to manage filters on parameters. There are two kinds of filters:
The one that compares two values, for instance: number of balloons > 5
The one that will say is a value is inside a range of value, for instance: parameter name is inside {"one", "two", "three", "seven"}.
screenshot of my JTable:
As we can see in the picture, when there is the "comparator" "is among", we would need a JComboBox in cell[0][2] to choose the values of the range within a complete set of fields.
While cell[1][2] does not need a JComboBox, but just an editable cell.
I hope I have been clear and thank you for your help.
EDIT:
I was able to display a JComboBox only to realize, I couldn't select multiple values on it. So now I am trying to display a JList instead of a ComboBox.
But when I click on the cell, the JList is not displayed, I don't know why.
Here is my code:
JTable tableParametersFilter = new JTable(modelParametersFilter){
// Determine editor to be used by row
public TableCellEditor getCellEditor(int row, int column)
{
int modelColumn = convertColumnIndexToModel( column );
int modelRow = convertRowIndexToModel( row );
Parameter_Filter pf = view.listParameter_Filter.get(modelRow);
if(modelColumn == 2 && pf instanceof Parameter_Filter_To_List_Of_Fields) {
Parameter_Filter_To_List_Of_Fields pftlof = (Parameter_Filter_To_List_Of_Fields)pf;
JList<String> list = new JList<String>(pftlof.list_of_fields_total_names);
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
list.setLayoutOrientation(JList.VERTICAL_WRAP);
list.setVisibleRowCount(-1);
return new TableCellEditor() {
#Override
public boolean stopCellEditing() {
return false;
}
#Override
public boolean shouldSelectCell(EventObject anEvent) {
return false;
}
#Override
public void removeCellEditorListener(CellEditorListener l) {
}
#Override
public boolean isCellEditable(EventObject anEvent) {
return true;
}
#Override
public Object getCellEditorValue() {
return list.getSelectedValuesList().toString();
}
#Override
public void cancelCellEditing() {
}
#Override
public void addCellEditorListener(CellEditorListener l) {
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return list;
}
};
}
return super.getCellEditor(row, column);
}
};
Any suggestions?
I have solved my problem.
I have not been able to add multiple choice JComboBox, or a displayable JList on the Cell of the Jtable.
Instead, I have used a JOptionPane that displayed a JList.
Here's the code:
tableParametersFilter.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JTable target = (JTable)e.getSource();
int row = target.getSelectedRow();
int column = target.getSelectedColumn();
if(column == 2){
Parameter_Filter pf = view.listParameter_Filter.get(row);
if(pf instanceof Parameter_Filter_To_List_Of_Fields) {
Parameter_Filter_To_List_Of_Fields pftlof = (Parameter_Filter_To_List_Of_Fields) pf;
JList<String> jlist = new JList<String>(pftlof.list_of_fields_total_names);
String StringOfIntArray = (String) tableParametersFilter.getValueAt( row, 2);
int[] list_parameter_id = Statique.StringOfIntArrayToIntegerArray(StringOfIntArray);
if(list_parameter_id.length < jlist.getModel().getSize()) {
int[] list_places = pftlof.getPlaceOfParameters(list_parameter_id);
for(int i = 0; i < list_places.length; i++) {
jlist.setSelectedIndices(list_places);
}
}
JScrollPane scrollPane = new JScrollPane(jlist);
scrollPane.setPreferredSize( new Dimension( 500, 500 ) );
JOptionPane.showMessageDialog(
null, scrollPane, "Multi-Select Example", JOptionPane.PLAIN_MESSAGE);
int[] SelectedIndices = jlist.getSelectedIndices();
Integer[] listParametersId = new Integer[SelectedIndices.length];
for(int i = 0; i < SelectedIndices.length; i++) {
int id = pftlof.list_of_fields_Total[SelectedIndices[i]].id;
try {
Parameter p = Parameter.getParameter(
id,
Parameter_Filter_To_List_Of_Fields.getTotal_Parameter_In_Parameter_Filter_To_List_Of_Fields());
listParametersId[i] = p.id;
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
}
}
System.out.println(Arrays.toString(listParametersId));
tableParametersFilter.setValueAt(Arrays.toString(listParametersId), row, 2);
}
}
}
}
I am working on netbeans and creating a swing application. I have created a JComboBox and a JTable. I am able to add value from JComboBox to JTable on a button click but if I repeat the same process the same value is again again added to the table. How to stop adding the existed value of JComboBox.
This is the code of JComboBox
private void populateCombo(){
organizationComboBox.removeAllItems();
for (Organization.Type type : Organization.Type.values()){
organizationComboBox.addItem(type);
}
}
This is the code of JTable
private void populateTable(){
DefaultTableModel model = (DefaultTableModel) organizationTable.getModel();
model.setRowCount(0);
for (Organization organization : organizationDirectory.getOrganizationList()){
Object[] row = new Object[2];
row[0] = organization.getOrganizationID();
row[1] = organization.getName();
model.addRow(row);
}
}
This is the code for my add button
Type type = (Type) organizationComboBox.getSelectedItem();
Organization o = organizationDirectory.createOrganization(type);
if(type.equals(Type.Receptionist)){
o.getSupportedRole().add(new ReceptionistRole());
}else
if(type.equals(Type.Doctor)){
o.getSupportedRole().add(new DoctorRole());
}else
if(type.equals(Type.VaccineManager)){
o.getSupportedRole().add(new VaccineManagerRole());
}else
if(type.equals(Type.LabAssistant)){
o.getSupportedRole().add(new LabAssistantRole());
}else
if(type.equals(Type.Donor)){
o.getSupportedRole().add(new DonorRole());
}else
if(type.equals(Type.Patient)){
o.getSupportedRole().add(new PatientRole());
}
populateTable();
Thanks in advance.
Don't use DefaultTableModel. This class is only for simple cases and demo applications. Simply look here for example of your own model.
So your model will looks like:
public class OrganizationModel extends AbstractTableModel {
protected String[] columnNames;
protected List<Organization> dataVector;
public OrganizationModel(String[] columnNames) {
this.columnNames = columnNames;
dataVector = new ArrayList<Organization>();
}
public String getColumnName(int column) {
return columnNames[column];
}
public boolean isCellEditable(int row, int column) {
return false;
}
public Class getColumnClass(int column) {
return String.class;
}
public Object getValueAt(int row, int column) {
return column == 0? dataVector.get(row).getOrganizationID() : dataVector.get(row).getName();
}
public void addRowWhenNotExist(Organization o) {
if (!dataVector.contains(o)) {
dataVector.add(o);
fireTableRowsInserted(dataVector.size() - 1, dataVector.size() - 1);
}
}
}
For correct working of this example you also need correct definition of methods equals and hashCode for your class Organization.
public class Organization {
// your stuff
public boolean equals(Object another) {
if (another instanceof Organization) {
return getOrganizationID() == ((Organization) another).getOrganizationID();
} else {
return false;
}
}
public int hashCode() {
return getOrganizationID();
}
}
I have many different array lists. I want each index to be a new row in the JTable but I'm not sure how to do that. I made a for loop but it is not working.
Is there even a way to populate a JTable with an array list and not an array?
public TableCreator() {
super(new GridLayout(1,0));
String[] columnNames = {"Item Type",
"Description",
"Size",
"Price"};
// for(int i=0; i<ShoppingFunctions.cartType.size(); i++){
for(int i=0; i<GUI.size.size(); i++){//FIX!!!!
item = ShoppingFunctions.cartType.get(i)+"\n";
described = GUI.describe[GUI.imageNum];
sizes = GUI.size.get(i);
price = ShoppingFunctions.cartPrice.get(i)+"\n";
}//end of for
Object[][] data = {{item, described, sizes, price}};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
if (DEBUG){
table.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
printDebugData(table);
}//end of method
});//end of listener
}//end of if
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}//end of method
The simple answer is to create your own, based on something like AbstractTableModel
For example...
public abstract class AbstractGenericTableModel<R> extends AbstractTableModel {
protected ArrayList<R> rows;
protected List columnIdentifiers;
public AbstractGenericTableModel() {
this(0, 0);
}
public AbstractGenericTableModel(int rowCount, int columnCount) {
this(new ArrayList(columnCount), rowCount);
}
public AbstractGenericTableModel(List columnNames, int rowCount) {
setData(new ArrayList<>(rowCount), columnNames);
}
public AbstractGenericTableModel(List<R> data, List columnNames) {
setData(data, columnNames);
}
public List<R> getRowData() {
return rows;
}
private List nonNullVector(List v) {
return (v != null) ? v : new ArrayList();
}
public void setData(List<R> data, List columnIdentifiers) {
this.rows = new ArrayList<>(nonNullVector(data));
this.columnIdentifiers = nonNullVector(columnIdentifiers);
fireTableStructureChanged();
}
public void addRow(R rowData) {
insertRow(getRowCount(), rowData);
}
public void insertRow(int row, R rowData) {
rows.add(row, rowData);
fireTableRowsInserted(row, row);
}
/**
* Removes the row at <code>row</code> from the model. Notification of the row being removed will be sent to all the listeners.
*
* #param row the row index of the row to be removed
* #exception ArrayIndexOutOfBoundsException if the row was invalid
*/
public void removeRow(int row) {
rows.remove(row);
fireTableRowsDeleted(row, row);
}
public void setColumnIdentifiers(List columnIdentifiers) {
setData(rows, columnIdentifiers);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return columnIdentifiers.size();
}
#Override
public String getColumnName(int column) {
Object id = null;
// This test is to cover the case when
// getColumnCount has been subclassed by mistake ...
if (column < columnIdentifiers.size() && (column >= 0)) {
id = columnIdentifiers.get(column);
}
return (id == null) ? super.getColumnName(column)
: id.toString();
}
}
public class ArrayListTableModel extends AbstractGenericTableModel<ArrayList> {
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
List<ArrayList> rows = getRowData();
return rows.get(rowIndex).get(columnIndex);
}
}
This creates two classes, an "abstract generics" based model, which allows you to specify the physical data which backs a row and a ArrayListTableModel which allows you to use a ArrayList for the individual data.
What this assumes though, is each row has the same number of elements, but it makes no checks
I suggest you have a closer look at How to Use Tables for more details
You can use the List Table Model. It will support rows of data stored in an ArrayList, Vector or any other class that implements the List interface.
The ListTableModel is also based on a generic TableModel that allows you to add Objects to a row in the model. There are many row based methods that allow easy usage of the model
The ListTableModel provides methods that allow you to easily customize the model:
setColumnClass – specify the class of individual columns so the proper renderer/editor can be used by the table.
setModelEditable – specify editable property for the entire model.
setColumnEditable – specify editable property at a column level. This property will have preference over the model editable property.
The other option is to iterate through the ArrayList and copy each row of data to a Vector and then add the Vector to the DefaultTableModel using the addRow(...) method.
I am trying to add a JButton as the first column on the table I created. I did make a research and couldn't find any solution for the tables that use abstract table model.
Here, I create an object array for each record that has texts and boolean variables to have the table render check boxes. Then those object arrays saved into an ArrayList
Here's my code to create my table data.
public ArrayList<Object[]> setTableData(){
/*
* ItemInfo fields
**********************
* line[0] - ReferenceNo
* line[1] - Quantity
* line[2] - ItemNameDescriptionSKU
* line[3] - Cube
*/
//Setting the data for the table
ArrayList<Object[]> itemList = new ArrayList<>();
for (int i=0; i<this.itemInfo.size();i++){
Object[] tempArray = new Object[7];
tempArray[0] = this.itemInfo.get(i)[1]; //Quantity
tempArray[1] = this.itemInfo.get(i)[2].toUpperCase(); //Item description
tempArray[2] = this.itemInfo.get(i)[3]; //Cube
//This adds charges if the payment type is COD
//To not to write the charge amount for every row
//checks the COD type only at the first record of items
if (i==0 && this.invoice[8].equals("COD"))
tempArray[3] = this.invoice[22]; //Charges if the invoice type is COD, null otherwise
else
tempArray[3] = " ";
tempArray[4] = new Boolean (false); //Loaded
tempArray[5] = new Boolean (false); //Delivered (Will be ignored if pickup)
itemList.add(tempArray);
}
return itemList;
Here's my table model
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class TicketTableModel extends AbstractTableModel {
private ArrayList<Object[]> data;
private boolean isDelivery;
private String[] columns;
public TicketTableModel(ArrayList<Object[]> itemInfo, boolean isDelivery){
super();
this.data = itemInfo;
this.isDelivery = isDelivery;
}
#Override
public String getColumnName(int i) {
return this.columns[i];
}
public void setColumns ( String[] columns1 ){
this.columns = columns1;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return columns.length;
}
#Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
if (col < 3)
return false;
else
return true;
}
#Override
public void setValueAt(Object value, int row, int col) {
data.get(row)[col] = value;
fireTableCellUpdated(row, col);
}
#Override
public Object getValueAt(int row, int col) {
return this.data.get(row)[col];
}
Take a look at Table Button Column.
This class implements the render/editor needed to make the button functional. You also provide the Action to invoke when the button is pressed.
The type of TableModel is irrelevant. If you want to "show" a button on a JTable, you supply a TableCellRenderer (and probably a TableCellEditor) for the column which is capable of rendering the button based on the values of the column it represents.
This will mean that your TableModel will need to support a column within it's model which represents the button.
Take a closer look at Using Custom Renderers, Using Other Editors and How to Use Tables
I have a JTable displaying rows from an SQL database. The table is relatively small (only 4 columns and up to 1000 rows).
I would like to give the user the opportunity to edit any cells in the table but want to avoid restricting it so much so that they must use an edit dialog box (this makes for far easier error checking and validation but is less intuitive)
I have tried a few different ways of controlling edit selections using the valueChanged method of my JTable but haven't had much luck.
I would like each row to be edited and written to the database at the conclusion of editing. I would like that once a cell has been clicked to start the editing of that row, no other rows can be selected until the user has finished editing the row (other rows are grayed out). After editing each cell and pressing enter, the edit selection should jump to the next column in the same row.
Can anyone give pointers on how I can achieve this?
// Create table with database data
table = new JTable(new DefaultTableModel(data, columnNames)) {
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;
}
#Override
public boolean isCellEditable(int row, int col){
return true;
}
#Override
public boolean editCellAt(int row, int column) {
boolean ans = super.editCellAt(row, column);
if (ans) {
Component editor = table.getEditorComponent();
editor.requestFocusInWindow();
}
return ans;
}
#Override
public void valueChanged(ListSelectionEvent source) {
super.valueChanged(source);
if (table!=null)
table.changeSelection(getSelectedRow(), getSelectedColumn()+1, false, false);
}
};
Edit - custom cell editor with table pointer seems to be a start
public class ExchangeTableCellEditor extends AbstractCellEditor implements TableCellEditor {
private JTable table;
JComponent component = new JTextField();
public ExchangeTableCellEditor(JTable table) {
this.table = table;
}
public boolean stopCellEditing() {
boolean ans = super.stopCellEditing();
//now we want to increment the cell count
table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()+1);
return ans;
}
#Override
public void cancelCellEditing() {
//do nothing... must accept cell changes
}
#Override
public Object getCellEditorValue() {
return ((JTextField)component).getText();
}
#Override
public Component getTableCellEditorComponent(JTable arg0, Object value,
boolean arg2, int arg3, int arg4) {
((JTextField)component).setText((String)value);
return component;
}
}
The default renderer and editor is typically adequate for most data types, but you can define custom renderers and editors as needed.
Addendum: I'm unfamiliar with the approach shown in your fragment. Instead, register a TableModelListener with your model, as shown below, and update the database with whatever granularity is warranted. See also How to Use Tables: Listening for Data Changes.
Addendum: #kleopatra is correct about your TableCellEditor. One convenient way to notify listeners is to invoke the super implementation, as shown here. Note that the delegate invokes fireEditingStopped().
/** #see https://stackoverflow.com/questions/9155596 */
public class NewJavaGUI extends JPanel {
private final JTable table;
public NewJavaGUI() {
String[] colNames = {"C1", "C2", "C3"};
DefaultTableModel model = new DefaultTableModel(colNames, 0) {
#Override
public boolean isCellEditable(int row, int col) {
// return your actual criteria
return true;
}
#Override
public Class getColumnClass(int col) {
// return your actual type tokens
return getValueAt(0, col).getClass();
}
};
// Add data; note auto-boxing
model.addRow(new Object[]{"A1", "A2", 42});
model.addRow(new Object[]{"B1", "B2", 42d});
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
// DML as indicated
}
});
table = new JTable(model);
this.add(table);
}
private void display() {
JFrame f = new JFrame("NewJavaGUI");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new NewJavaGUI().display();
}
});
}
}
The behaviour you mention can be achieved by forcing your table to start editing again.
First make sure you now yourRow and Column and that you add your own tablecelleditor that extands from the AbstractCellEditor
then add this to your stopCellEditing method:
EventQueue.invokeLater(new Runnable()
{
public void run()
{
yourTable.editCellAt( yourRow, yourColumn+1);
}
});