I'm coding small schedule of visits. It bases on JTable with custom TableModel extending AbstractTableModel as shown on image below.
The problem is that it is possible to initializate Table and to get desired look - but after data change using setValueAt()
there is no change in appearance of Table. Button click takes sample String and sets fields in columns "Type" and "State" at 9:00 (more precisely depending on given hour and date of reservation)
What is more I'am able to insert new row at the end of table but cannot make visible existing value update. Tried many solutions but sadly without result. I would be very grateful for every suggestions.
Working code illustrating the problem:
import java.awt.EventQueue;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ModelTest {
private JFrame frame;
private JTable tablePendingVisits;
private PendingVisitModel pendingVisitModel;
private JScrollPane scrollPanePendingVisits;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ModelTest window = new ModelTest();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ModelTest() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 407);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JButton btnChangeValue = new JButton("Change value at 9:00");
btnChangeValue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
refreshTableModel();
}
});
btnChangeValue.setBounds(63, 308, 305, 23);
frame.getContentPane().add(btnChangeValue);
tablePendingVisits = new JTable();
scrollPanePendingVisits = new JScrollPane();
pendingVisitModel = new PendingVisitModel();
tablePendingVisits.setModel(pendingVisitModel);
scrollPanePendingVisits.setBounds(63, 36, 305, 246);
scrollPanePendingVisits.setViewportView(tablePendingVisits);
frame.getContentPane().add(scrollPanePendingVisits);
}
public void refreshTableModel(){
String[] sampleString = {"9:00", "Bobby", "Tables"};
// search for row with 9:00 and replace values in given columns
for (int i = 0; i < pendingVisitModel.getRowCount(); i++) {
if( sampleString[0].equals(pendingVisitModel.getValueAt(i, 0)) ) { // Change row values when both hours are equal
pendingVisitModel.setValueAt(sampleString[1], i, 1); // Change at type column
pendingVisitModel.setValueAt(sampleString[2], i, 2); // Change at status column
}
}
}
}
// Custom TableModel
class PendingVisitModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private String[] columnNames = {"HOUR", "TYPE", "STATE"};
private Vector<String[]> data = new Vector<String[]>();
public PendingVisitModel() {
for(int i = 8; i<15; i++) {
data.add(new String[]{i+":00", "-", "Free"} );
data.add(new String[]{i+":15", "-", "Free"} );
data.add(new String[]{i+":30", "-", "Free"} );
data.add(new String[]{i+":45", "-", "Free"} );
}
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.size();
}
public String getColumnName(int col) {
return columnNames[col];
}
public String getValueAt(int row, int col) {
String[] temp = data.get(row);
if(temp.length > 0 && col < 3)
return temp[col];
else
return null;
}
public void setValueAt(String[] value, int row, int col) {
String[] temp = data.get(row);
temp[col] = value[col];
data.set(row, temp);
fireTableRowsUpdated(row, row);
}
public void insertRow(String[] value) {
data.add(value);
fireTableRowsInserted(data.size(), data.size());
}
public void clearRows(){
data.clear();
fireTableDataChanged();
}
public void removeAllEntry(){
data.clear();
fireTableDataChanged();
}
public boolean isCellEditable(int row, int col) {
return false;
}
@Override
public Class<String> getColumnClass(int colNum) {
return String.class;
}
}
There is a difference between public void setValueAt(String[] value, int row, int col) {
and public void setValueAt(Object value, int row, int col) {
You can test this by using the @Override
annotation, for example...
@Override
public void setValueAt(String[] value, int row, int col) {
Generates a compiler error, where as
@Override
public void setValueAt(Object value, int row, int col) {
does not.
So changing the setValueAt
method to...
@Override
public void setValueAt(Object value, int row, int col) {
if (value instanceof String) {
String[] temp = data.get(row);
temp[col] = value.toString();
data.set(row, temp);
fireTableRowsUpdated(row, row);
}
}
Worked just find for me
Also, you really should avoid using null
layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
As noted in the comments, you could also use public void setValueAt(String value, int row, int col) {
(no override annotation) and your code would work, but I wouldn't call the method setValueAt
as that generates confusion (as you've discovered) and precludes the scenarios where all you have is an instance of TableModel
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.