Repainting in a multithreaded environment
i am working on an applet with around ten different datasources(e.g. statistics/error-log/...). Each datasource is updated by a single network connection and reports updates via the observer mechanism. The applet has different views which display parts of the data. Every view is only interested in some parts of the data and registers itself as an Observer at the necessary Observables.
The views(extended JPanels) mostly consist of standard swing components (e.g. JLabels, JButton, ...). Some attributes of the components in the views depend on information from the underlying data model.
Example:
StatisticPanel::paintComponent(Graphics g) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
super.paintComponent(g);
}
This logic is implemented in the paintComponent()
method of the StatisticPanel and the update()
methods just calls repaint(), because I didn't want the manipulate the components outside of the EDT.
Is this the intended way of updating swing components in a multi-threaded environment? Is开发者_C百科 it better to use a Runnable with SwingUtitlies.invokeLater()
? Are there better approaches for this problem?
I second camickr's recommendations, but regarding this code snippet:
StatisticPanel::paintComponent(Graphics g) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
super.paintComponent(g);
}
You have non-painting methods in your paintComponent method (the first two methods), and that shouldn't be as 1) you want this method to be as lean and fast as possible and thus have only painting-related code, and 2) you do not have aboslute control of when this method is called or even if it is called, and so non-painting related code and program logic does not belong in there. For these reasons, I strongly urge you to get them out of there, but instead should be called separate from paintComponent, but as with most Swing code, on the EDT.
EDIT 1
I'm not a professional, but how about if you gave your StaticPanel a method similar to this:
public void doMyUpdate() {
if (SwingUtilities.isEventDispatchThread()) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
}
});
}
repaint(); // if the paintComponent method has more than just a super call.
}
EDIT 2
Also, please have a look at this thread: check-if-thread-is-edt-is-necessary
repaint() is used to invoke the Swing RepaintManger which in turn will schedule the repainting of the component, so yes it is ok to just invoke repaint directly. The RepaintManager will make sure all repainting is done on the EDT.
精彩评论