I've spent a while reading and experimenting here, and come up with a few approaches, but not got any of them to work completely yet, so I would like to know what more experienced Swing programmers would do.
The main window of my application contains a custom subtype of JPanel, which is used to display an image calculated from a mathematical function. This can take some time to calculate, so while this happens I display a text message and a progress bar superimposed on the middle of the panel. I would like both the text and the progress bar to resize appropriately when the panel is resized, probably using a constant fraction of the width of the panel.
At the moment I have another JPanel subtype that contains the text and the progress bar, and I add this "progress panel" to the main panel, and set it to be visible when required. The main panel does not contain any other components, but has its own paintComponent() method to display a BufferedImage obtained from another class. There are a few problems with this as it stands:
- The progress panel is not always visible when it should be, perhaps because there is nowhere in the code that explicitly ensures it is painted in front of the image.
- The progress panel always takes up the full width of the application panel. It is centred vertically using a BoxLayout in the main panel with some vertical glue before and after the progress panel, but I haven't managed to get the same trick to work horizontally, and in any case the preferred horizontal size of the progress panel is not necessarily right. One approach that almost works is to use a BorderLayout with horizontal and vertical struts around the progress panel, but the size of the progress panel is still not right, and the struts need to be changed when the main panel is resized.
So what would you do?
- Use a different layout manager in the main panel?
- Use a LayeredPane or OverlayLayout somehow?
- Add a further layer to the containment hierarchy containing the image panel and the progress panel?
- Some combination of the above, or something else?
What about resizing the text. Do I need to explicitly create a new Font when the panel is resized, or is there any way to do this more automatically?
-
The preferred size of JProgressBar
is specified by the UI delegate, BasicProgressBarUI
. The example below illustrates the effect of various layout managers. FlowLayout
simply uses the UIManager
default, ProgressBar.horizontalSize
, while GridLayout
and BorderLayout.CENTER
fill the available space. BoxLayout
, with flanking glue, adjusts proportionally as the frame is resized.
I am already using a SwingWorker
Updating the GUI from the process()
method of your SwingWorker
should be safe. You can change layers or even remove components, but I'd be wary of anything overly complicated.
Addendum: Here's the relevant default.
System.out.println(UIManager.get("ProgressBar.horizontalSize"));
javax.swing.plaf.DimensionUIResource[width=146,height=12]
Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
/** @see http://stackoverflow.com/questions/7256775 */
public class ProgressTest {
private static final Color border = Color.gray;
private static void display() {
JFrame f = new JFrame("ProgressTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(0, 1));
f.add(createPanel(new FlowLayout()));
f.add(createPanel(new GridLayout()));
f.add(createPanel(new BorderLayout()));
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
p.setBorder(BorderFactory.createLineBorder(border));
JProgressBar jpb = new JProgressBar();
p.add(Box.createHorizontalGlue());
p.add(jpb);
p.add(Box.createHorizontalGlue());
jpb.setIndeterminate(true);
f.add(p);
f.pack();
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JPanel createPanel(LayoutManager layout) {
JPanel p = new JPanel();
p.setBorder(BorderFactory.createLineBorder(border));
p.setLayout(layout);
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(true);
p.add(jpb);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
display();
}
});
}
}
I know this is a layout question, but SwingWorker
might make the problem simpler if you have useful interim results. I sometimes start with this example.