问题
This application pulls data from a text file and inserts it into JTable
. An Observer
is attached to check every 300 ms if there is a change to the file and reload the data. I have the setChanged()
and notifyObservers()
in my Observer
class.
When data is added to the table, a getRowCount()
reports that the row was added, notifiers are operational. Virtually everything is working except for the repaint()
. I have tried revalidate()
and fireTableDataChanged()
all to no avail. I would appreciate some help on this. No compile errors are reported throughout the process.
Table Model
package HardwareDbFile;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.table.AbstractTableModel;
public class HardwareFileTableModel extends AbstractTableModel{
protected Vector data;
protected Vector columnNames ;
protected String datafile;
public HardwareFileTableModel(String file){
datafile = file;
initVectors();
}
public void initVectors() {
String aLine ;
data = new Vector();
columnNames = new Vector();
try {
FileInputStream fin = new FileInputStream(datafile);
try (BufferedReader br = new BufferedReader(new InputStreamReader(fin))) {
StringTokenizer st1 = new StringTokenizer(br.readLine(), "|");
while(st1.hasMoreTokens()) {
columnNames.addElement(st1.nextToken());
}
// extract data
while ((aLine = br.readLine()) != null) {
StringTokenizer st2 = new StringTokenizer(aLine, "|");
while(st2.hasMoreTokens()) {
data.addElement(st2.nextToken());
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getRowCount() {
return data.size() / getColumnCount();
}
@Override
public int getColumnCount(){
return columnNames.size();
}
@Override
public String getColumnName(int columnIndex) {
String colName = "";
if (columnIndex <= getColumnCount()) {
colName = (String)columnNames.elementAt(columnIndex);
}
return colName;
}
@Override
public Class getColumnClass(int columnIndex){
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (String)data.elementAt( (rowIndex * getColumnCount()) + columnIndex);
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
}
}
Observer Class
package HardwareDbFile;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.Timer;
public class HardwareFileObserver extends Observable implements ActionListener {
Timer time = new Timer(300,this); // check every 300ms
long lastModified;
String file ;
HardwareFileObserver (String string) {
file = string;
File f = new File(file);
lastModified = f.lastModified(); // original timestamp
time.start();
}
@Override
public void actionPerformed(ActionEvent e) {
File f = new File(file);
long actualLastModified = f.lastModified();
if (lastModified != actualLastModified) {
// the file have changed
lastModified = actualLastModified;
setChanged();
notifyObservers();
}
}
}
Main Class
public class HardwareInventoryUI extends javax.swing.JFrame implements Observer {
private String datafile = "hardware.dat";
private String dataFilePath = "C:\\Windows\\Temp\\hardware.dat";
protected HardwareFileTableModel model;
/**
* Creates new form HardwareInventoryUI
*/
public HardwareInventoryUI() {
initComponents();
model = new HardwareFileTableModel(dataFilePath);
HardwareFileObserver fileWD;
// this Observable object is monitoring any file change
fileWD = new HardwareFileObserver(dataFilePath);
fileWD.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
// reload data because data file have changed
model.initVectors();
jTable.repaint();
}
Add Record Button
private void jAddRecordActionPerformed(java.awt.event.ActionEvent evt) {
String toolID = jToolID.getText();
String toolName = jToolName.getText();
String quantity = jQuantity.getText();
String itemPrice = jItemPrice.getText();
try {
model = new HardwareFileTableModel(dataFilePath);
FileWriter fstream = new FileWriter(dataFilePath, true);
try (BufferedWriter newRecord = new BufferedWriter(fstream)) {
newRecord.newLine();
newRecord.write(toolID + "|"+ toolName + "|" + quantity + "|" + itemPrice );
}
} catch (IOException ex) {
Logger.getLogger(HardwareInventoryUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
回答1:
Your
TableModel
should implementObserver
, not your view. In theupdate()
method, updatedata
andfireTableDataChanged()
. AJTable
listens to itsTableModel
and updates itself automatically in response.Also consider alternate implementations of the observer pattern mentioned here.
As illustrated here, your implementation of
setValueAt()
is incorrect. You need to fire the event that would notify the listening table of the change:@Override public void setValueAt(Object value, int row, int column) { // update data fireTableCellUpdated(row, column); }
If the content of your
datafile
can be obtained as the standard output of a command, you may be able to eliminate the file using the approach outlined here.
Addendum: As @kleopatra comments, "Note that with a well-behaved model, you never need to put any thought into view updates, they simply happen auto-magically :-) Or the other way round: if manual repaints on a view (such as table, tree, list) seem be be a solution, the model implementation is incorrect."
来源:https://stackoverflow.com/questions/13661821/jtablerepaint-not-functioning-as-expected