Java Swing中的垂直堆叠布局?

时间:2018-02-14 11:35:53

标签: java swing layout-manager gridbaglayout boxlayout

我想在scrollPane中安排一些标签,以便它们一个堆叠在另一个之下,并且每个标签必须足够高(不多于!)以适合它包含的文本,并且作为相应的容器 标签的文本用html格式化以实现自动换行效果。

情况如下: schema

如果我水平调整框架的大小,视口的总高度应该增加或减少,因为文本分别有更少或更多的水平空间
我发现自己陷入困境,因为据我所知,布局管理者倾向于将组件放在可用空间中,而不是相反。
我使用了gridBagLayout来保持图像和右边描述之间的宽度比率。
在我看来,像wrap layout解决的问题,但从垂直的角度来看,这是基流布局没有考虑到的问题,
或者是一个boxLayout,它允许填充水平空间并重新计算首选高度。

这是屏幕: screen 如果您需要一些代码,请告诉我

所以,这就是我想要实现的目标,现在你建议采用什么方法?

修改1:

这是一个小例子,抱歉,如果需要一段时间,但建议我从头开始(我使用eclipse的窗口构建器)

package view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

public class Gui extends JFrame{

    private JPanel contentPane;
    private JScrollPane scrollPane;
    private JPanel panel;
    private JLabel lbThumbnail;
    private JPanel descriptions;
    private JPanel header;
    private JLabel lb1;
    private JLabel lb2;
    private JLabel lb3;
    private JLabel lb4;

    /**
     * Launch the application.
     */
    public static void main(String[] args){
        EventQueue.invokeLater(new Runnable(){
            @Override
            public void run(){
                try{
                    Gui frame=new Gui();
                    frame.setVisible(true);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public Gui(){
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100,100,564,365);
        contentPane=new JPanel();
        contentPane.setBorder(new EmptyBorder(5,5,5,5));
        contentPane.setLayout(new BorderLayout(0,0));
        setContentPane(contentPane);

        scrollPane = new JScrollPane();
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        contentPane.add(scrollPane, BorderLayout.CENTER);

        panel = new JPanel();
        panel.setBackground(Color.RED);
        panel.setBorder(null);
        scrollPane.setViewportView(panel);
        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));

        header = new JPanel();
        header.setBackground(Color.GREEN);
        header.setBorder(null);
        panel.add(header);
        GridBagLayout gbl_header = new GridBagLayout();
        gbl_header.columnWidths = new int[] {120, 46};
        gbl_header.rowHeights = new int[] {14};
        gbl_header.columnWeights = new double[]{0.0, 0.0};
        gbl_header.rowWeights = new double[]{0.0};
        header.setLayout(gbl_header);

        lbThumbnail = new JLabel("thumbnail");
        lbThumbnail.setBorder(new LineBorder(Color.GRAY, 3));
        lbThumbnail.setHorizontalAlignment(SwingConstants.CENTER);
        lbThumbnail.setBackground(Color.RED);
        GridBagConstraints gbc_lbThumbnail = new GridBagConstraints();
        gbc_lbThumbnail.fill = GridBagConstraints.BOTH;
        gbc_lbThumbnail.weightx = 0.3;
        gbc_lbThumbnail.weighty = 1.0;
        gbc_lbThumbnail.gridx = 0;
        gbc_lbThumbnail.gridy = 0;
        header.add(lbThumbnail, gbc_lbThumbnail);

        descriptions = new JPanel();
        descriptions.setBackground(Color.ORANGE);
        descriptions.setBorder(null);
        GridBagConstraints gbc_descriptions = new GridBagConstraints();
        gbc_descriptions.weightx = 0.7;
        gbc_descriptions.fill = GridBagConstraints.BOTH;
        gbc_descriptions.gridx = 1;
        gbc_descriptions.gridy = 0;
        header.add(descriptions, gbc_descriptions);
        descriptions.setLayout(new BoxLayout(descriptions, BoxLayout.PAGE_AXIS));

        lb1 = new JLabel("<html>y y y y y y y y y y y y y y y y y y</html>");
        lb1.setBorder(new LineBorder(Color.GRAY, 3));
        lb1.setHorizontalAlignment(SwingConstants.CENTER);
        lb1.setAlignmentY(Component.TOP_ALIGNMENT);
        lb1.setAlignmentX(Component.CENTER_ALIGNMENT);
        descriptions.add(lb1);

        lb2 = new JLabel("<html>this label should fill the container width, and then eventually increase it's height y y y y y y y y y y y y y y y y y y</html>");
        lb2.setBorder(new LineBorder(Color.GRAY, 3));
        lb2.setHorizontalAlignment(SwingConstants.CENTER);
        lb2.setAlignmentY(Component.TOP_ALIGNMENT);
        lb2.setAlignmentX(Component.CENTER_ALIGNMENT);
        descriptions.add(lb2);

        lb3 = new JLabel("<html>the same y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y</html>");
        lb3.setBorder(new LineBorder(Color.GRAY, 3));
        lb3.setHorizontalAlignment(SwingConstants.CENTER);
        lb3.setAlignmentY(Component.TOP_ALIGNMENT);
        lb3.setAlignmentX(Component.CENTER_ALIGNMENT);
        descriptions.add(lb3);

        lb4 = new JLabel("<html>the upper panel should be as short as it can be, now it's too long<br/>y<br/>y<br/>y<br/></html>");
        lb4.setAlignmentX(0.5f);
        lb4.setBorder(new LineBorder(Color.GRAY, 3));
        lb4.setHorizontalAlignment(SwingConstants.CENTER);
        panel.add(lb4);
    }

}

输出: gif

  • 顶部区域太高
  • 右侧标签不会调整宽度
  • 整个视口面板使用框架
  • 调整大小

2 个答案:

答案 0 :(得分:2)

您的原始图片具有误导性,因为看起来您的文字正在包装以适应视口的宽度,这是令人困惑的,因为通常文本在滚动窗格中显示时不会换行,因为它显示在其首选大小

但是,您的MCVE表明文本确实没有换行。所以问题不在于您的布局管理器,而是将面板的Scrollable接口的实现添加到滚动窗格中。默认行为是显示组件添加首选宽度/高度,并在需要时显示滚动条。

在你的情况下,你想强制&#34;宽度&#34;以适应视口的大小,从而使文本根据需要垂直包裹,然后根据需要显示垂直滚动条。这将导致标签的文本换行并动态重新计算高度(因为JLabel处理HTML的方式)。

因此,您需要为面板实现Scrollable接口,并覆盖getScrollableTracksViewportWidth()方法以返回“true”,这将强制面板的宽度适合视口的宽度滚动窗格,因此永远不会出现水平滚动条。

或者更简单的方法是使用Scrollable Panel,它具有允许您控制`Scrollable接口属性的方法。

使用上述课程,您可以按如下方式更改代码:

//panel = new JPanel();
ScrollablePanel panel = new ScrollablePanel();
panel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );

答案 1 :(得分:1)

我希望我没有误解你的问题,所以基本上你希望包装的文字标签使用可用的宽度并相应地调整它的高度,对吧?我不确定内置布局管理器是否可以解决您的问题,但在我看来,自定义布局管理器将是最简单的方法。

首先我们将框架作为测试驱动器,它有一个滚动窗格,其中包含应用自定义布局的面板。那个自定义布局需要知道&#34;组件所以我们只是将它们传递给构造函数:

public class MainFrame extends JFrame {
    private final JTextPane txt1 = createTextPane();
    private final JTextPane txt2 = createTextPane();
    private final JTextPane txt3 = createTextPane();

    public MainFrame() {
        JPanel panel = new JPanel();

        panel.setLayout(new CustomLayout(txt1, txt2, txt3));

        panel.add(txt1);
        panel.add(txt2);
        panel.add(txt3);

        add(new JScrollPane(panel));

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setBounds(10, 10, 100, 100);
    }

我使用文本窗格来允许自动换行功能:

private static JTextPane createTextPane() {
    JTextPane result = new JTextPane();

    result.setContentType("text/html");
    result.setEditable(false);
    result.setText("<html>Hello World Hello World Hello World Hello World Hello World Hello World</html>");

    return result;
}

然后是CustomLayout的代码。最重要的方法是如何布局组件并返回首选父级大小。这个想法是:

  • 将组件的宽度设置为可用宽度(您设置的高度无关紧要,因此我只传递零),获得首选高度将是包装内容的高度。
  • 请注意,我使用父母的父级来获取可用宽度,因为父级尚未布局,因此其宽度不正确,而父级的父级已经布局。您可以说这是一个危险的假设,因此它实际上取决于组件层次结构和实际情况的结构。

    public class CustomLayout implements LayoutManager {
        private final JTextPane txt1;
        private final JTextPane txt2;
        private final JTextPane txt3;
    
    public CustomLayout(JTextPane aTxt1, JTextPane aTxt2, JTextPane aTxt3) {
        txt1 = aTxt1;
        txt2 = aTxt2;
        txt3 = aTxt3;
    }
    
    @Override
    public void addLayoutComponent(String name, Component comp) {
    }
    
    @Override
    public void removeLayoutComponent(Component comp) {
    }
    
    @Override
    public Dimension preferredLayoutSize(Container parent) {
        int width = parent.getParent().getWidth();
    
        int height = 0;
    
        height += calculateComponentPreferredHeightForWidth(txt1, width);
        height += calculateComponentPreferredHeightForWidth(txt2, width);
        height += calculateComponentPreferredHeightForWidth(txt3, width);
    
        return new Dimension(width, height);
    }
    
    private static int calculateComponentPreferredHeightForWidth(JComponent component, int width) {
        component.setSize(width, 0);
        return component.getPreferredSize().height;
    }
    
    @Override
    public Dimension minimumLayoutSize(Container parent) {
        return preferredLayoutSize(parent);
    }
    
    @Override
    public void layoutContainer(Container parent) {
        int width = parent.getWidth();
    
        int layoutX = 0;
        int layoutY = 0;
    
        txt1.setBounds(layoutX, layoutY, width, calculateComponentPreferredHeightForWidth(txt1, width));
        layoutY = txt1.getY() + txt1.getHeight();
    
        txt2.setBounds(layoutX, layoutY, width, calculateComponentPreferredHeightForWidth(txt2, width));
        layoutY = txt2.getY() + txt2.getHeight();
    
        txt3.setBounds(layoutX, layoutY, width, calculateComponentPreferredHeightForWidth(txt3, width));
    }
    

    }