Java Swing:固定宽度和可变高度的垂直布局

3
我正在寻找一种布局管理器(或一组布局管理器),可以帮助我垂直排列我的组件。所有组件(按钮)都应具有相同的宽度,由底层对话框客户端区域的宽度边界限定。每个按钮都能根据连接的JLabel(标签将在用户单击相关按钮后显示)扩展其高度 - 简而言之:所有组件应该被拉伸到相同的固定宽度,但高度因其内容而异。整个对话框内容应放置在一个JScrollPane中,以支持需要时的垂直滚动条。

我已经尝试了很多不同的布局管理器(大部分是BorderLayout用于水平拉伸和BoxLayout用于垂直排列),以实现我想要的行为。但代码既不令人满意,也不能完全按照我的意愿工作。

我花了很多时间在互联网上搜索这个问题,并发现在Java中,垂直布局是一个常见的问题。很多人回复说GridBagLayout是最好的布局来完成这项工作,但我没有让它按照我想要的方式工作。

希望这些信息足够为我解决这个问题提供一些帮助。

最好的问候 Michael

编辑:-不再需要-

编辑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);
    }
}

如果您在这些按钮和标签上发布一个代码示例,会更容易一些。 - undefined
不可能的。JScrollPane的调整大小行为与BoxLayout和GridBagLayout的调整大小行为相冲突。 - undefined
1
请查看来自SwingXVerticalLayout - undefined
@MadProgrammer:如果我更了解Apache Pivot,我会尝试使用Pivot来实现这个功能。 - undefined
@FrecherxDachs FYI:为了使您的代码具有语法高亮显示,不要使用诸如<code>之类的标签,而是选择您的代码并在编辑器工具栏中使用按钮{} - undefined
显示剩余4条评论
4个回答

3

谢谢,我知道这些示例,正如我在第一篇帖子中所说的那样,这就是我的当前代码的样子,但是盒式布局有一些疯狂的拉伸行为。 - undefined
这是当前结果的图像,但在调整大小时会出现一些奇怪的行为。http://www.pic-upload.de/view-16978954/example-image.png.html - undefined
尝试设置按钮的首选大小 - undefined
1
@SriHarshaChilakapati 不要在任何组件上设置setPreferredSize。这真的是一个糟糕的建议。设置首选大小会阻止处理不同的L&F、不同的字体等等... 在几乎所有情况下(至少对于基本的Swing小部件),您不应该需要强制指定首选大小。将此留给布局管理器和XxxUI处理。 - undefined
@GuillaumePolet 谢谢你让我知道这个。之前没听说过。 - undefined

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);
}

谢谢,我已经尝试过这个了,但排列不正确。示例如下。 - undefined
你能提供一张关于布局有问题的图片吗?过去我的经验是,只要进行足够的调整,你就可以让GridBag做任何你需要的事情。 - undefined
@FrecherxDachs 在最后一个位置上添加一个 weighty 为 1 的 "fill" 控件,这将把其他组件推到容器的顶部。我通常使用一个 JPanel,或者如果我想让容器支持透明度,我会使用一个没有文本的 JLabel - undefined
我尝试了你的示例,看起来不错。但最终我遇到了与当前解决方案相同的问题:我将整个GridBagLayout放入一个ScrollPane中,以便在需要时进行垂直滚动,但我使用的JLabels(包含HTML信息)在对话框的右侧不换行,因此GridBagLayout会被水平拉伸。所以有没有一种方法可以告诉GridBagLayout(或者滚动窗格的可视区域),它的最大宽度是多少(应该与对话框的宽度相同)? - undefined
我不知道,我猜可能是因为JScrollPane的原因。 - undefined
显示剩余2条评论

1

这里有几个问题:

  1. JFrameBorderLayout 会拉伸你的 JPanel p
  2. 你没有设置 weightx,因此 fill 属性是无用的

解决方案:

  1. 要么将你的 JPanel p 包装在另一个面板中,将其放置在顶部,要么添加一个额外的不可见 JPanel 在末尾来推动你的组件
  2. 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似乎不起作用。有什么想法吗? - undefined
1
@FrecherxDachs 你不需要设置preferredSize/maximumSize等等...(实际上,你几乎不需要设置它们)。如果我添加一个JScrollPane,就没有任何问题。当窗口高度太小时,垂直滚动条会出现。如果还是不起作用,请发布另一个问题并附上你更新后的代码。记住, - undefined
谢谢您的回复,我认为我的主题是正确的,问题在于滚动窗格视口的宽度有时比底层对话框的宽度要大(因为内容的原因)。您能看到我的问题吗?这里再次附上当前的行为:http://www.pic-upload.de/view-16988005/example-image3.png.html - undefined
@FrecherxDachs 在你的代码中更新你的JScrollPane和图片中显示的文本框,我在你的代码中没有看到。 - undefined

1

好的,我解决了。我完全错了,我以为JScrollPane是这个问题的罪魁祸首,但实际上是JLabel。如果JLabel包含HTML文本,它不会自动换行,这就是为什么如果对话框的宽度太小,所有内容都会水平拉伸。我在这里找到了一个很好的解决方案:通过设置最大宽度使JLabel自动换行

答案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的所需宽度。
非常感谢您提供的所有有用提示。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接