[英]Vertical stacked layout in Java Swing?
我想在scrollPane中安排一些标签,以便它们一个堆叠在另一个之下,并且它们中的每一个都必须足够高(不多于!)以适合它包含的文本,并且与相应的容器一样宽 。
标签的文本使用html格式化以实现自动换行效果。
如果我水平调整框架的大小,视口的总高度应该增加或减少,因为文本分别具有更少或更多的水平空间 。
我发现自己陷入困境,因为据我所知,布局管理者倾向于将组件放在可用空间中,而不是相反。
我使用了gridBagLayout来保持图像和右边描述之间的宽度比。
在我看来,这是一个问题,如包裹布局解决的问题,但从垂直的角度来看,这是基流布局没有考虑到的,
或者是一个boxLayout,它允许填充水平空间并重新计算首选高度。
所以,这就是我想要实现的目标,现在你建议采用什么方法?
编辑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);
}
}
您的原始图片具有误导性,因为看起来您的文字正在包装以适应视口的宽度,这是令人困惑的,因为通常文本在滚动窗格中显示时不会换行,因为它以其首选大小显示。
但是,您的MCVE
显示文本确实没有换行。 所以问题不在于您的布局管理器,而是添加到滚动窗格的面板的Scrollable
接口的实现。 默认行为是显示组件添加首选宽度/高度,并在需要时显示滚动条。
在您的情况下,您希望强制“宽度”适合视口的大小,因此导致文本根据需要垂直包裹,然后根据需要显示垂直滚动条。 这将导致标签的文本换行并动态重新计算高度(因为JLabel处理HTML的方式)。
因此,您需要为面板实现Scrollable
接口并覆盖getScrollableTracksViewportWidth()
方法以返回“true”,这将强制面板的宽度适合滚动窗格视口的宽度,因此水平滚动条将永远不会出现。
或者更简单的方法是使用Scrollable Panel ,它具有允许您控制`Scrollable接口属性的方法。
使用上面的类,您可以按如下方式更改代码:
//panel = new JPanel();
ScrollablePanel panel = new ScrollablePanel();
panel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
我希望我没有误解你的问题,所以基本上你希望包装的文本标签使用可用的宽度并相应地调整它的高度,对吧? 我不确定内置布局管理器是否可以解决您的问题,但在我看来,自定义布局管理器将是最简单的方法。
首先,我们将框架作为测试驱动器,它有一个滚动窗格,其中包含应用自定义布局的面板。 该自定义布局需要“知道”组件,因此我们只需将它们传递给构造函数:
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)); }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.