I order to develop a swing application, I am using a simplified version of the MVC pattern, in which the controller is interposed between the view and the model in both directions:
- when the user interacts with a swing control, if this interaction requires access to the model, then the event raised by the swing control calls for an appropriate method of controller;
- when the view should be updated after a model update, the controller invokes one or more public methods of the view.
A complex application may request different threads running: for example, if you must write a file on disk, you could start a background thread, and at the end of the writing of the file, this thread should send a notification to view via the controller. In this case, it may happen that more than one thread wants to refresh the view, so I assume that this issue should be handled in some way.
I took inspiration from this answer in order to write the methods appendText
and updateLastText
of the following SSCCE.
public class NewJFrame extends javax.swing.JFrame {
private volatile String lastText;
/**
* Creates new form NewJFrame
*/
public NewJFrame() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new JScrollPane();
jTextArea1 = new JTextArea();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jTextArea1.setEditable(false);
jTextArea1.setColumns(20);
jTextArea1.setRows(5);
jScrollPane1.setViewportView(jTextArea1);
getContentPane().add(jScrollPane1, BorderLayout.CENTER);
pack();
}// </editor-fold>
// Variables declaration - do not modify
private JScrollPane jScrollPane1;
private JTextArea jTextArea1;
// End of variables declaration
/**
*
* @param text
*/
public void appendText(final String text) {
updateLastText(text);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jTextArea1.append(String.format("%s\n", lastText));
}
});
}
/**
*
* @param text
*/
private synchronized void updateLastText(String text) {
lastText = text;
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
final NewJFrame frame = new NewJFrame();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
frame.setVisible(true);
}
});
Thread counter = new Thread() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
frame.appendText((new Date()).toString());
}
}
};
counter.start();
}
}
The above solution seems to work properly, so should I use the same technique to update other swing component of the UI? Or is there a more compact solution?