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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13476274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 12:55:01  来源:igfitidea点击:

Java Swing: Vertical Layout with fixed width and variable height

javaswinglayoutvertical-alignmentfixed-width

提问by FrecherxDachs

I'm looking for a layout manager (or set of layout managers) that help me to arrange my components vertically. All components (buttons) should have the same width, bordered by the underlaying dialog client area width. Each of these buttons is able to expand its height depending on the connected JLabel (the labels will be displayed once the user clicked on the related button) - in short: all the components should be stretched to the same fixed width, but with a variable height depending on their content. The whole dialog content should be placed within a JScrollPane to support a vertical scrollbar if needed.

我正在寻找一个布局管理器(或一组布局管理器)来帮助我垂直排列我的组件。所有组件(按钮)应具有相同的宽度,以底层对话框客户区宽度为边界。这些按钮中的每一个都能够根据连接的 JLabel 扩展其高度(一旦用户单击相关按钮,标签将显示) - 简而言之:所有组件都应拉伸到相同的固定宽度,但具有变量高度取决于它们的内容。如果需要,整个对话框内容应放置在 JScrollPane 中以支持垂直滚动条。

I'm at the point, where I stacked a lot of different layout managers (mostly borderlayout for horizontal stretching and boxlayout for vertical arrangement) to implement my aimed behaviour. But it's neither satisfying code nor does it work completely as I want.

我到了这一点,我堆叠了许多不同的布局管理器(主要是用于水平拉伸的边框布局和用于垂直排列的框布局)来实现我的目标行为。但这既不是令人满意的代码,也不能完全按照我的意愿工作。

I spent a lot of time surfing the internet for this problem and discovered that vertical layouts are a common problem in Java. A lot of people replied that GridBagLayout would be the best layout doing this job, but I didn't get it to work the way I wanted.

为了这个问题,我花了很多时间在网上冲浪,发现垂直布局是Java中的一个常见问题。很多人回答说 GridBagLayout 将是完成这项工作的最佳布局,但我没有让它以我想要的方式工作。

I hope that is enough information to give me some help for that problem.

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

Best regards Michael

最好的问候迈克尔

edit: -not needed anymore-

编辑:-不再需要-

edit 2:

编辑2:

Here is an image of my current attempt: http://www.pic-upload.de/view-16978954/example-image.png.html

这是我当前尝试的图像:http: //www.pic-upload.de/view-16978954/example-image.png.html

This is almost the result i want, but has strange beviour on resizing.

这几乎是我想要的结果,但在调整大小时有奇怪的行为。

edit 3:

编辑3:

I think my main problem is the JScrollPane combined with a html containing JLabel, I need something like setMaximumSize(...), but it doesn't work as it should: http://www.pic-upload.de/view-16988005/example-image3.png.html

我认为我的主要问题是 JScrollPane 与包含 JLabel 的 html 相结合,我需要像 setMaximumSize(...) 这样的东西,但它不能正常工作:http: //www.pic-upload.de/view- 16988005/example-image3.png.html

I need a fixed width, but i cannot find a way to set it.

我需要一个固定的宽度,但我找不到设置它的方法。

setPreferredSize(...) works, but I don't know the height of the JLabel, because it contains html text.

setPreferredSize(...) 有效,但我不知道 JLabel 的高度,因为它包含 html 文本。

edit 4:

编辑4:

my Button:

我的按钮:

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

the frame:

框架:

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

采纳答案by FrecherxDachs

OK, I got it to work. I was completely wrong, I thought the JScrollPane was the trouble maker for this issue, but it was the JLabel. If a JLabel contains HTML text, it won't wrap the text automatically, that's why everything got stretched horizontally, if the dialog's width was too little. I found a good solution for this problem here: make a JLabel wrap it's text by setting a max width

好的,我让它工作了。我完全错了,我认为 JScrollPane 是这个问题的麻烦制造者,但它是 JLabel。如果 JLabel 包含 HTML 文本,它不会自动换行文本,这就是为什么如果对话框的宽度太小,所有内容都会被水平拉伸。我在这里找到了解决此问题的好方法:通过设置最大宽度使 JLabel 包裹它的文本

Answer 6:

答案 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);

Everytime the dialog is resized, it is necessary to update the width of each HTML containing JLabel to the desired width using the code above.

每次调整对话框大小时,都需要使用上面的代码将每个包含 JLabel 的 HTML 的宽度更新为所需的宽度。

Thank you very much for all your useful tips.

非常感谢您提供的所有有用提示。

回答by Sri Harsha Chilakapati

回答by zbrunson

What I would use is a GridBagLayout. It is akin to an HTML table, and by using GridBagConstraintsyou can set the objects to expand in both the horizontal and vertical direction (ex. constraints.fill = GridBagConstraints.BOTH)

我将使用的是GridBagLayout。它类似于 HTML 表格,通过使用GridBagConstraints,您可以将对象设置为在水平和垂直方向上展开(例如。constraints.fill = GridBagConstraints.BOTH

I believe this fixes your issue with in the picture below (there is a small hack to make it the buttons stay on the top by adding a blank label at the bottom, but I'm sure that with more tinkering this can be removed)

我相信这可以解决您在下图中的问题(有一个小技巧可以通过在底部添加一个空白标签来使按钮保持在顶部,但我相信通过更多的修补可以将其删除)

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

回答by Guillaume Polet

Several issues here:

这里有几个问题:

  1. the BorderLayoutof the JFramewill stretch your JPanel p
  2. you don't set weightx, therefore the fillattribute is useless
  1. BorderLayoutJFrame会伸展你的JPanel p
  2. 你没有设置weightx,因此该fill属性是无用的

Solutions:

解决方案:

  1. Either wrap your JPanel pin another panel that will put it at the top, either add an extra invisible JPanel at the end to push your component
  2. set the weightxproperty to 1.0
  1. 要么将您JPanel p的面板包裹在另一个面板中,将其放在顶部,要么在末尾添加一个额外的不可见 JPanel 以推动您的组件
  2. weightx属性设置为1.0

Here is a demo code (based on yours) which works:

这是一个有效的演示代码(基于您的):

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

}