tl;dr: I want to do what's in the second picture (ignore the red lines)
I understand how GroupLayout works, but I can't figure this out, or whether it's even possible. I initially had this code:
#Horizontal layout is a parallel group containing 3 sequences of components
layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) #everything else to the right
.addGroup(layout.createSequentialGroup() #row 1
.addComponent(startTimeLabel)
.addComponent(self.lastUpdatedLabel))
.addGroup(layout.createSequentialGroup() #row 2
.addComponent(self.progressBar)
.addComponent(self.clearBtn))
.addGroup(layout.createSequentialGroup() #row 3
.addComponent(self.fileProgLabel)
.addComponent(self.sizeProgLabel)
.addComponent(self.ETCLabel))))
which produced this:
However, I want to align the 2 top labels at the start and end of the progress bar, and the 3 bottom labels at the start, middle, and end of the progress bar, like this (mspainted):
My first approach at this was to try to split the components into the parallel groups I've made with the lines above. I put struts either side of the progress bar, aligning the 4 end labels to those, and aligned the center label to the progress bar itself:
#Horizontal layout is a sequence of 3 parallel groups of components, and an end component
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) #starttime, strut, filesprog
.addComponent(startTimeLabel)
.addComponent(progLeft) #invisible, just for gluing things to
.addComponent(self.fileProgLabel))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER) #progress bar, sizeprog
.addComponent(self.progressBar)
.addComponent(self.sizeProgLabel))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING) #updatetime, strut, ETC
.addComponent(self.lastUpdatedLabel)
.addComponent(progRight) #invisible, just for gluing things to
.addComponent(self.ETCLabel))
.addComponent(self.clearBtn))
However, as I expected, this forced the progress bar to squeeze into the horizontal space between the top 2 labels, like this:
Finally, I thought of getting rid of the struts, and adding the progress bar to three separate parallel groups: aligned LEADING
with the two left labels, CENTER
with the middle label, and TRAILING
with the right label:
#Horizontal layout is a sequence of 4 parallel groups of components
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) #starttime, progleft, filesprog
.addComponent(startTimeLabel)
.addComponent(self.progressBar)
.addComponent(self.fileProgLabel))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER) #progmid, sizeprog
.addComponent(self.progressBar)
.addComponent(self.sizeProgLabel))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING) #updatetime, progright, ETC
.addComponent(self.lastUpdatedLabel)
.addComponen开发者_StackOverflow社区t(self.progressBar)
.addComponent(self.ETCLabel))
.addComponent(self.clearBtn))
However, Swing clearly ignores the second and third mentions of the progress bar, and I end up with this:
I reckon I've had a pretty decent go at this, and I'm well and truly out of ideas. Is there any way to make a component span multiple parallel groups?
Given your requirements, I would rather use a GridBagLayout
. It's a bit more complex but I wrote a piece of code that does what you want.
class T extends JFrame {
public T() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
String s1 = "Started at: 2011-09-12 15:33:38";
String s2 = "Last updated: 2011-09-12 15:33:44";
String s3 = "File copied:2/10";
String s4 = "Bytes copied: 234/1000";
String s5 = "ETC: 2011-09-02 15:34:02";
JProgressBar progressBar = new JProgressBar();
progressBar.setMinimum(100);
progressBar.setStringPainted(true);
progressBar.setString("23%");
progressBar.setValue(23);
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
c.insets = new Insets(5, 5, 5, 5);
add(new JLabel(s1), c);
c.gridx = 2;
add(new JLabel(s2, JLabel.RIGHT), c);
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 3;
add(progressBar, c);
c.gridx = 3;
add(new JButton("Clear"), c);
c.gridx = 0;
c.gridy = 2;
add(new JLabel(s3), c);
c.gridx = 0;
add(new JLabel(s4, JLabel.CENTER), c);
c.gridx = 2;
add(new JLabel(s5, JLabel.RIGHT), c);
setSize(600, 300);
setVisible(true);
}
public static void main(String[] args) {
new T();
}
Here is the result:
How would you nest it?
As shown below.
Seems like that would get overly-complicated pretty quickly.
It depends on the requirements.
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
/** @see http://stackoverflow.com/questions/7279799 */
public class NestedLayout extends Box {
private String s1 = "Started at: 2011-09-12 15:33:38";
private String s2 = "Last updated: 2011-09-12 15:33:44";
private String s3 = "File copied:2/10";
private String s4 = "Bytes copied: 234/1000";
private String s5 = "ETC: 2011-09-02 15:34:02";
private JProgressBar pb = new JProgressBar();
public NestedLayout(int axis) {
super(axis);
JPanel top = new JPanel(new GridLayout());
top.add(new JLabel(s1, JLabel.LEFT));
top.add(new JLabel(s2, JLabel.RIGHT));
JPanel mid = new JPanel(new GridLayout());
pb.setMaximum(100);
pb.setStringPainted(true);
pb.setString("23%");
pb.setValue(23);
mid.add(pb);
JPanel bot = new JPanel(new GridLayout());
bot.add(new JLabel(s3, JLabel.LEFT));
bot.add(new JLabel(s4, JLabel.CENTER));
bot.add(new JLabel(s5, JLabel.RIGHT));
Box east = new Box(BoxLayout.Y_AXIS);
east.add(Box.createVerticalGlue());
east.add(new JButton("Clear"));
east.add(Box.createVerticalGlue());
JPanel center = new JPanel(new GridLayout(0, 1));
center.add(top);
center.add(mid);
center.add(bot);
this.add(center);
this.add(east);
}
private void display() {
JFrame f = new JFrame("NestedLayout");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new NestedLayout(BoxLayout.X_AXIS).display();
}
});
}
}
It's obviously too late, but I found this question while I was looking for something similar, so someone else might find this answer useful.
This answer actually uses GroupLayout. The only thing in here which I consider a hack is setting a preferred size to the scrollbar (if you take it out, the labels will overlap.)
It's simpler than you might have suspected:
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class LayoutTest implements Runnable {
private JLabel startTimeLabel = new JLabel("start time");
private JLabel lastUpdatedLabel = new JLabel("last updated");
private JLabel fileProgLabel = new JLabel("file progress");
private JLabel sizeProgLabel = new JLabel("size progress");
private JLabel etcLabel = new JLabel("etc");
private JButton clearBtn = new JButton("Clear");
private JProgressBar progressBar = new JProgressBar(new DefaultBoundedRangeModel(27, 0, 0, 100));
public static void main(String[] args) {
SwingUtilities.invokeLater(new LayoutTest());
}
@Override
public void run() {
progressBar.setStringPainted(true);
JPanel panel = new JPanel();
GroupLayout layout = new GroupLayout(panel);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
panel.setLayout(layout);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(progressBar, GroupLayout.DEFAULT_SIZE, 500, GroupLayout.DEFAULT_SIZE)
.addComponent(startTimeLabel, GroupLayout.Alignment.LEADING)
.addComponent(fileProgLabel, GroupLayout.Alignment.LEADING)
.addComponent(sizeProgLabel, GroupLayout.Alignment.CENTER)
.addComponent(lastUpdatedLabel, GroupLayout.Alignment.TRAILING)
.addComponent(etcLabel, GroupLayout.Alignment.TRAILING))
.addComponent(clearBtn));
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(startTimeLabel)
.addComponent(lastUpdatedLabel))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(progressBar)
.addComponent(clearBtn))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(fileProgLabel)
.addComponent(sizeProgLabel)
.addComponent(etcLabel)));
JFrame frame = new JFrame("Layout Test");
frame.setContentPane(panel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
There is probably a way to use a parallel group for the LEADING and TRAILING component pairs, but the layout completely rearranged when I tried to do that.
Sometimes the solution to GroupLayout problems is to ignore the vertical layout when doing the horizontal layout and ignore the horizontal layout when doing the vertical layout. Flatten the layout to a single dimension (rearranging similar rows and columns next to each other helps) and write the layout to match the 1D picture in your head.
I would particularly recommend against using GridBagLayout, boxes and the like, because getting the correct spacing between components in a cross-platform fashion is a nightmare.
精彩评论