Java Swing:具有固定宽度和可变高度的垂直布局
Posted
技术标签:
【中文标题】Java Swing:具有固定宽度和可变高度的垂直布局【英文标题】:Java Swing: Vertical Layout with fixed width and variable height 【发布时间】:2012-11-08 16:50:05 【问题描述】:我正在寻找可以帮助我垂直排列组件的布局管理器(或一组布局管理器)。所有组件(按钮)都应具有相同的宽度,并以底层对话框客户区宽度为边界。这些按钮中的每一个都能够根据连接的 JLabel 扩展其高度(一旦用户单击相关按钮,标签将显示) - 简而言之:所有组件都应拉伸到相同的固定宽度,但具有变量高度取决于它们的内容。如果需要,整个对话框内容应放置在 JScrollPane 中以支持垂直滚动条。
我正处于这一点,我堆叠了许多不同的布局管理器(主要是用于水平拉伸的边界布局和用于垂直排列的框布局)来实现我的目标行为。 但它既不是令人满意的代码,也不能完全按照我的意愿工作。
我花了很多时间在网上浏览这个问题,发现垂直布局是 Java 中的一个常见问题。很多人回答说 GridBagLayout 将是完成这项工作的最佳布局,但我没有让它按照我想要的方式工作。
我希望这些信息足以帮助我解决这个问题。
最好的问候 迈克尔
编辑:-不再需要-
编辑 2:
这是我当前尝试的图像: http://www.pic-upload.de/view-16978954/example-image.png.html
这几乎是我想要的结果,但在调整大小时有奇怪的行为。
编辑 3:
我认为我的主要问题是 JScrollPane 与包含 JLabel 的 html 相结合,我需要 setMaximumSize(...) 之类的东西,但它不能正常工作: http://www.pic-upload.de/view-16988005/example-image3.png.html
我需要一个固定的宽度,但我找不到设置它的方法。
setPreferredSize(...) 有效,但我不知道 JLabel 的高度,因为它包含 html 文本。
编辑 4:
我的按钮:
public class Expander extends JPanel implements ActionListener
private static class HtmlViewer extends JLabel
private static final long serialVersionUID = 8787130155299083869L;
public HtmlViewer(String doc)
super(doc);
this.setBackground(Color.WHITE);
this.setOpaque(true);
private static final long serialVersionUID = 4480221797736558685L;
JToggleButton button;
JComponent component;
public Expander(String text, String doc)
this(text, new HtmlViewer(doc));
public Expander(String text, JComponent expandableComponent)
this.setLayout(new BorderLayout());
button = new JToggleButton(text)
private static final long serialVersionUID = -3330376265192275758L;
@Override
public void paint(Graphics g)
super.paint(g);
if (this.isSelected())
g.drawString("▼", this.getWidth() - 20, 15);
else
g.drawString("■", this.getWidth() - 20, 15);
;
button.setFocusPainted(false);
button.addActionListener(this);
component = expandableComponent;
component.setBorder(new EmptyBorder(5, 5, 5, 5));
this.add(button, BorderLayout.PAGE_START);
@Override
public void actionPerformed(ActionEvent e)
if (button.isSelected())
this.add(component);
else
this.remove(component);
this.getTopLevelAncestor().validate();
this.getTopLevelAncestor().repaint();
this.setMaximumSize(this.getPreferredSize());
框架:
public class grid2
public static void main(String[] args)
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
JScrollPane scroll = new JScrollPane(p, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
addStuff(p);
frame.add(scroll);
frame.pack();
frame.setVisible(true);
public static void addStuff(Container container)
container.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.weighty = 0;
Expander e = new Expander("test", "<html>fgb fdh fgfhg ifghiufdshfidsghfiufdsghiudsfhdsfiu dshiufhds if dhf idsufhdsiufhiufhiuds hfdshfiudshfuidsifudshfiudshf ufdhfiushdfiudshiufhdsiufhdsiuf udshfiudshfiudshfudshf iuhfiudshfiudshfiudshf</html>"); // long html text example
e.setMaximumSize(new Dimension(500, 10000));
c.gridx = 0;
c.gridy = 0;
c.gridwidth = GridBagConstraints.REMAINDER;
container.add(e, c);
JButton button2 = new JButton("Button 2");
c.gridy = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
container.add(button2, c);
c.gridy = 2;
c.weighty = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
container.add(new JLabel(), c);
【问题讨论】:
如果您发布带有这些按钮和标签的代码示例会更容易。 我可以发布一两张图片,因为结果应该是这样的,但我不知道发布图片的正确降价。 :// 添加了图片链接,希望这样也可以 不可能。 JScrollPane 的大小调整行为与 Box 布局和 GridBagLayout 的大小调整行为发生冲突。 从SwingX看VerticalLayout
【参考方案1】:
见http://docs.oracle.com/javase/tutorial/uiswing/layout/box.html
setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
【讨论】:
谢谢,我知道这些示例,正如我在第一篇文章中所说,这就是我当前的代码的样子,但是盒子布局有一些疯狂的拉伸行为。 这是当前结果的图像,但在调整大小时会出现一些奇怪的行为。 pic-upload.de/view-16978954/example-image.png.html 尝试设置按钮的首选大小 @SriHarshaChilakapati 不,不要在任何组件上设置PreferredSize。这真是一个糟糕的建议。设置首选尺寸可以防止处理不同的 L&F、不同的字体等...在几乎所有情况下(至少对于基本的 Swing 小部件),您不需要强制使用首选尺寸。把它留给 LayoutManagers 和 XxxUI。 @GuillaumePolet 感谢您让我知道这一点。以前没听说过。【参考方案2】:我会使用GridBagLayout。它类似于 HTML 表格,通过使用GridBagConstraints,您可以将对象设置为在水平和垂直方向上展开(例如constraints.fill = GridBagConstraints.BOTH
)
我相信这可以解决您在下图中的问题(有一个小技巧可以通过在底部添加一个空白标签使按钮保持在顶部,但我相信通过更多的修补可以做到这一点删除)
public static void main(String[] args)
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addStuff(frame.getContentPane());
frame.pack();
frame.setVisible(true);
public static void addStuff(Container container)
container.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.weighty = 0;
JButton button = new JButton("Button 1");
c.gridx = 0;
c.gridy = 0;
container.add(button, c);
JButton button2 = new JButton("Button 2");
c.gridy = 1;
container.add(button2, c);
c.gridy = 2;
c.weighty = 1;
container.add(new JLabel(), c);
【讨论】:
谢谢,我已经试过了,但安排不正确。示例如下。 你能提供一张关于布局不正确的图片吗?过去,根据我的经验,只要进行足够的调整,您就可以让 GridBag 做几乎任何您需要的事情 @FrecherxDachs 您将“填充”控件添加到最后一个位置,weighty
为 1,这会将其他组件推到容器顶部。我通常使用JPanel
,或者如果我希望容器支持透明度,则使用不带文本的JLabel
我试过你的例子,看起来不错。但我最终遇到了与当前解决方案相同的问题:我将整个 GridBagLayout 放在 ScrollPane 中,以便在需要时启用垂直滚动,但我使用了 JLabels(包含 html 信息),它不会包裹在右侧对话框,因此 GridBagLayout 将被水平拉伸。那么有没有办法说GridBagLayout(或滚动窗格视口)的最大宽度是多少(应该与对话框宽度相同)?
您应该能够set the maximum size 应用布局的组件。【参考方案3】:
这里有几个问题:
JFrame
的BorderLayout
将拉伸您的JPanel p
你没有设置weightx
,所以fill
属性没用
解决方案:
-
要么将您的
JPanel p
包装在另一个面板中,将其放在顶部,要么在末尾添加一个额外的不可见 JPanel 以推送您的组件
将weightx
属性设置为1.0
这是一个有效的演示代码(基于你的):
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Gridbag
public void initUI()
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel p = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
JButton button = new JButton("button 1");
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.PAGE_START;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.weightx = 1.0;
p.add(button, constraints);
button = new JButton("button 2");
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.PAGE_START;
p.add(button, constraints);
JPanel root = new JPanel(new GridBagLayout());
constraints.weighty = 1.0;
root.add(p, constraints);
frame.add(root);
frame.setVisible(true);
public static void main(String[] args)
SwingUtilities.invokeLater(new Runnable()
@Override
public void run()
new Gridbag().initUI();
);
【讨论】:
谢谢,这里也一样,基本上可以按我的意愿工作,但是如果我把它放在 JScrollPane 中,标签会水平拉伸它,所以我需要设置我喜欢的宽度,而不是高度。 setMaximumSize 似乎无法按预期与 JScrollPane 一起工作。有什么想法吗? @FrecherxDachs 您不需要设置 preferredSize/maximumSize/etc...(实际上您几乎不应该设置它们)。如果我添加一个 JScrollPane,我没有任何问题。当框架高度太小时出现垂直滚动条。如果它不起作用,请使用更新的代码发布另一个问题。记住, 感谢您的回复,我认为我的主题是正确的,问题是滚动窗格视口的宽度有时大于底层对话框的宽度(因为内容)。你看到我的问题了吗?又是当前的行为:pic-upload.de/view-16988005/example-image3.png.html【参考方案4】:好的,我让它工作了。我完全错了,我认为 JScrollPane 是这个问题的麻烦制造者,但它是 JLabel。如果 JLabel 包含 HTML 文本,它不会自动换行,这就是如果对话框的宽度太小,所有内容都会水平拉伸的原因。我在这里找到了解决这个问题的好方法:make a JLabel wrap it's text by setting a max width
答案 6:
JLabel labelBeingUsed = myLabel;
View view = (View) labelBeingUsed.getClientProperty(BasicHTML.propertyKey);
view.setSize(scrollPane1.getWidth(), 0.0f);
float w = view.getPreferredSpan(View.X_AXIS);
float h = view.getPreferredSpan(View.Y_AXIS);
labelBeingUsed.setSize((int) w, (int) h);
每次调整对话框大小时,都需要使用上面的代码将每个包含 JLabel 的 HTML 的宽度更新为所需的宽度。
非常感谢您提供的所有有用提示。
【讨论】:
以上是关于Java Swing:具有固定宽度和可变高度的垂直布局的主要内容,如果未能解决你的问题,请参考以下文章