如何使用 SpringLayout

Note:

本课涵盖了手工编写布局代码,这可能会很困难。如果您不希望了解布局 管理 的所有详细信息,则可能更喜欢将GroupLayout布局 管理 器与构建器工具结合使用来布局 GUI。 NetBeans IDE是此类构建器工具之一。否则,如果您想手工编码并且不想使用GroupLayout,那么推荐使用GridBagLayout作为下一个最灵活,功能最强大的布局 管理 器。

如果您对使用 JavaFX 创建 GUI 感兴趣,请参阅在 JavaFX 中使用布局

在 JDK 1.4 版中添加了SpringLayout类,以支持 GUI 构建器中的布局。 SpringLayout是一种非常灵活的布局 管理 器,可以模仿其他布局 管理 器的许多功能。 SpringLayout是非常底层的,因此您真的应该只将其与 GUI 构建器一起使用,而不是try手动编写 spring 布局 管理 器。

本节从一个简单的示例开始,显示创建第一个 Spring 布局时需要记住的所有内容-忘记它们会发生什么!稍后,它将介绍 Util 方法,使您可以将组件布置在几种不同类型的网格中。

以下是一些我们将介绍的布局的图片:

SpringBox 应用程序使用 SpringLayout 产生类似于 BoxLayout 产生的东西。

SpringForm 应用程序具有 5 行的标签文本字段对。

SpringCompactGrid 应用程序在网格中显示组件,而不必强制所有组件具有相同的大小。

Spring Layout 的工作方式

Spring 布局通过定义组件边缘之间的方向关系或约束来完成其工作。例如,您可以定义一个组件的左边缘与另一组件的右边缘之间的距离为固定距离(例如 5 像素)。

SpringLayout中,每个边的位置仅取决于另一个边的位置。如果随后添加了约束以为边创建新的绑定,则先前的绑定将被丢弃,并且该边仍然依赖于单个边。

与许多布局 管理 器不同,SpringLayout不会自动设置其 管理 的组件的位置。如果您手工编码使用SpringLayout的 GUI,请记住通过限制西/东和北/南位置来初始化组件位置。根据您使用的约束,您可能还需要显式设置容器的大小。

组件定义* edge 属性,这些属性由Spring个实例连接。每个 Spring 具有四个属性-其 minimum preferred maximum 值,以及其实际(当前) value *。与每个组件关联的 Spring 被收集到SpringLayout.Constraints对象中。

Spring类的实例具有表征其行为的三个属性:最小值,首选值和最大值。这些属性中的每一个都可能涉及基于一系列规则定义其第四值属性。

Spring类的实例可以可视化为机械 Spring,该机械 Spring 在压缩或拉伸远离其首选值时会提供校正力。该力被建模为距最佳值的距离的线性函数,但是具有两个不同的常数-一个用于压缩力,一个用于拉伸力。这些常数由 Spring 的最小值和最大值指定,以使处于最小值的 Spring 产生与处于最大值时所产生的力相等且相反的力。因此,优选值与最小值之间的差表示 Spring 可以被压缩的容易程度。它的最大值和首选值之间的差异表明Spring可以轻松扩展。

基于此,SpringLayout可以可视化为一组对象,这些对象通过边缘上的一组 Spring 连接。

Example: SpringDemo

本节将引导您完成为使用SpringLayout的容器指定约束的典型步骤。第一个示例SpringDemo1.java是一个非常简单的应用程序,它在由 Spring 布局控制的内容窗格中具有标签和文本字段。以下是相关代码:

public class SpringDemo1 {
    public static void main(String[] args) {
        ...
        Container contentPane = frame.getContentPane();
        SpringLayout layout = new SpringLayout();
        contentPane.setLayout(layout);
        contentPane.add(new JLabel("Label: "));
        contentPane.add(new JTextField("Text field", 15));
        ...
        frame.pack();
        frame.setVisible(true);
    }
}

单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringDemo1.另外,要自己编译和运行示例,请查阅example index

以下是首次出现时的 GUI 外观:

SpringDemo1-父级没有初始大小!

将其调整为更大尺寸时的外观如下:

SpringDemo1-所有组件都位于(0,0)!

显然,我们有一些问题。不仅框架抬起得太小,而且即使将其调整大小,组件也都位于(0,0)。发生这种情况是因为我们没有设置 Spring 来指定组件的位置和容器的宽度。一个小小的 console 是,至少这些组件的尺寸是首选的-我们免费获得SpringLayout为每个组件创建的默认 Spring。

我们的下一个示例SpringDemo2.java通过为每个组件指定位置来稍微改善这种情况。单击启动按钮使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringDemo2.另外,要自己编译和运行示例,请参考example index

在此示例中,我们将指定组件应显示在一行中,并且它们之间应有 5 个像素。以下代码指定标签的位置:

//Adjust constraints for the label so it's at (5,5).
layout.putConstraint(SpringLayout.WEST, label,
                     5,
                     SpringLayout.WEST, contentPane);
layout.putConstraint(SpringLayout.NORTH, label,
                     5,
                     SpringLayout.NORTH, contentPane);

第一个putConstraint调用指定标签的左(西)边应与其容器的左边相距 5 像素。这将转换为* x 坐标 5.第二个putConstraint调用在标签的顶部(北边)与其容器之间构建了相似的关系,从而导致 y *坐标为 5.

这是设置文本字段位置的代码:

//Adjust constraints for the text field so it's at
//(<label's right edge> + 5, 5).
layout.putConstraint(SpringLayout.WEST, textField,
                     5,
                     SpringLayout.EAST, label);
layout.putConstraint(SpringLayout.NORTH, textField,
                     5,
                     SpringLayout.NORTH, contentPane);

第一个putConstraint调用使文本字段的左(西)边与标签的右(东)边相距 5 像素。第二个putConstraint调用与第一个片段中的第二个调用相似,并且具有将组件的* y *坐标设置为 5 的效果。

前面的示例仍然存在容器体积过小的问题。但是,当我们调整窗口大小时,组件位于正确的位置:

SpringDemo2-至少现在所有组件都在正确的位置!

为了使容器最初显示为正确的尺寸,我们需要设置 Spring 以定义容器本身的右(东)和底(南)边缘。默认情况下,未设置容器右边缘和下边缘的约束。通过设置这些约束来定义容器的大小。 SpringDemo3.java显示了如何执行此操作。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringDemo3.另外,要自己编译和运行示例,请参考example index

这是设置容器 Spring 的代码:

layout.putConstraint(SpringLayout.EAST, contentPane,
                     5,
                     SpringLayout.EAST, textField);
layout.putConstraint(SpringLayout.SOUTH, contentPane,
                     5,
                     SpringLayout.SOUTH, textField);

第一个putConstraint调用使容器的右边缘位于文本字段右边缘的右侧 5 个像素。第二个使其底部边缘比最高组件的底部边缘高 5 个像素(为简单起见,我们假定其为文本字段)。

最后,窗口以正确的大小显示:

SpringDemo3-父级现在具有正确的初始大小!

当我们将窗口放大时,我们可以看到 Spring 的布局,在可用组件之间分配了额外的空间。

SpringDemo3 enlarged

在这种情况下,Spring 布局选择为文本字段提供所有额外的空间。尽管似乎 Spring 布局对待标签和文本字段的方式有所不同,但是 Spring 布局对 Swing 或 AWT 组件没有特殊的了解。它依赖于组件的最小,首选和最大大小属性的值。下一节讨论 Spring 布局如何使用这些属性,以及为什么它们会导致空间分布不均匀。

Spring 和 Component 尺寸

SpringLayout对象自动为SpringLayout控制的每个组件的高度和宽度安装Spring。这些 Spring 实质上是组件的getMinimumSizegetPreferredSizegetMaximumSize方法的封面。所谓“覆盖”,是指不仅用这些方法中的适当值对 Spring 进行了“初始化”,而且还对这些值进行了“跟踪”。例如,代表组件宽度的Spring对象是一种特殊的 Spring,它仅将其实现委派给组件的相关大小方法。这样,随着元件特性的变化,Spring 与尺寸方法保持同步。

当组件的getMaximumSizegetPreferredSize方法返回相同的值时,SpringLayout将此解释为意味着不应拉伸该组件。 JLabelJButton是以此方式实现的组件的示例。因此,SpringDemo3 示例中的标签不会拉伸。

某些组件(例如JTextField)的getMaximumSize方法针对其最大大小的宽度和高度返回值Integer.MAX_VALUE,指示该组件可以增 Long 到任何大小。因此,当放大 SpringDemo3 窗口时,SpringLayout将所有多余的空间分配给唯一可以增 Long 的 Spring-那些确定文本字段大小的 Spring。

有关 SpringLayout API 的更多信息

SpringDemo 示例使用SpringLayout方法putConstraint设置与每个组件关联的 Spring。 putConstraint方法是一种便捷方法,可让您无需使用完整的 Spring 布局 API 即可修改组件的约束。再次是SpringDemo3中的代码,该代码设置标签的位置:

layout.putConstraint(SpringLayout.WEST, label,
                     5,
                     SpringLayout.WEST, contentPane);
layout.putConstraint(SpringLayout.NORTH, label,
                     5,
                     SpringLayout.NORTH, contentPane);

以下是直接使用SpringLayout.ConstraintsSpring类的等效代码:

SpringLayout.Constraints  contentPaneCons = 
        layout.getConstraints(contentPane);
contentPaneCons.setX(Spring.sum(Spring.constant(5),
                          contentPaneCons
                          .getConstraint(SpringLayout.WEST)));
contentPaneCons.setY(Spring.sum(Spring.constant(5),
                          contentPaneCons
                          .getConstraint(SpringLayout.NORTH)));

要查看转换为使用此 API 的整个演示,请查看SpringDemo4.java。该文件还包含设置容器大小的代码的更优美(且更 Long)的版本。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringDemo4.另外,要自己编译和运行示例,请参考example index

正如前面的代码片段所暗示的,SpringLayoutSpringLayout.Constraints倾向于使用不同的约定来描述 Spring。 SpringLayout API 使用边来定义其约束。Spring 连接边缘以在它们之间构建线性关系。边缘由组件使用以下常量定义:

  • SpringLayout.NORTH指定组件边界矩形的顶部边缘。

  • SpringLayout.SOUTH指定组件边界矩形的底边。

  • SpringLayout.EAST指定组件边界矩形的右边缘。

  • SpringLayout.WEST指定组件边界矩形的左边缘。

  • SpringLayout.BASELINE指定组件的基线。

  • SpringLayout.HORIZONTAL_CENTER指定组件边界矩形的水平中心。

  • SpringLayout.VERTICAL_CENTER指定组件边界矩形的垂直中心。

边缘与Spring对象不同SpringLayout.Constraints类了解边缘,但以下属性仅具有Spring对象:

  • x

  • y

  • width

  • height

每个Constraints对象在其 Spring 和它们表示的边缘之间保持以下关系:

west = x
north = y
 east = x + width
south = y + height

如果您感到困惑,请不要担心。下一部分将介绍 Util 方法,您可以使用它们来完成一些常见的布局任务,而无需了解任何有关 spring layout API 的知识。

网格的 Util 方法

由于SpringLayout类是为 GUI 构建器创建的,因此为布局设置各个 Spring 可能需要手动编写代码。本节介绍了几种方法,可用来安装在网格中布置一组组件所需的所有 Spring。这些方法模拟GridLayoutGridBagLayoutBoxLayout类的某些功能。

SpringUtilities.java中定义了称为makeGridmakeCompactGrid的两种方法。两种方法都通过将组件分组为行和列,并使用Spring.max方法制作宽度或高度 Spring,该 Spring 使行或列足以容纳其中的所有组件。在makeCompactGrid方法中,相同的宽度或高度 Spring 分别用于特定列或行中的所有组件。相反,在makeGrid方法中,宽度和高度 Spring 由容器中的每个组件共享,从而迫使它们的大小都相同。此外,Spring提供了工厂方法来创建不同种类的 Spring,包括依赖于其他 Spring 的 Spring。

让我们看看这些方法的作用。我们在源文件SpringGrid.java中实现的第一个示例在文本字段中显示了一堆数字。中心文本字段比其他文本字段宽得多。就像GridLayout一样,拥有一个大单元将迫使所有单元都同样大。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringGrid。另外,要自己编译和运行示例,请参考example index

SpringGrid

这是在 SpringGrid 中创建和布置文本字段的代码:

JPanel panel = new JPanel(new SpringLayout());
for (int i = 0; i < 9; i++) {
    JTextField textField = new JTextField(Integer.toString(i));
    ...//when i==4, put long text in the text field...
    panel.add(textField);
}
...
SpringUtilities.makeGrid(panel,
                         3, 3, //rows, cols
                         5, 5, //initialX, initialY
                         5, 5);//xPad, yPad

现在让我们来看一个示例,在源文件SpringCompactGrid.java中,它使用makeCompactGrid方法而不是makeGrid。本示例显示大量数字,以展示 Spring 布局使所需空间最小化的能力。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringCompactGrid。另外,要自己编译和运行示例,请参考example index

这是 SpringCompactGrid GUI 的样子:

SpringCompactGrid

这是在 SpringCompactGrid 中创建和布置文本字段的代码:

JPanel panel = new JPanel(new SpringLayout());

int rows = 10;
int cols = 10;
for (int r = 0; r < rows; r++) {
    for (int c = 0; c < cols; c++) {
        int anInt = (int) Math.pow(r, c);
        JTextField textField =
                new JTextField(Integer.toString(anInt));
        panel.add(textField);
    }
}

//Lay out the panel.
SpringUtilities.makeCompactGrid(panel, //parent
                                rows, cols,
                                3, 3,  //initX, initY
                                3, 3); //xPad, yPad

makeCompactGrid方法最方便的用法之一是将标签与组件相关联,其中标签在一列中,而组件在另一列中。 SpringForm.java文件以这种方式使用makeCompactGrid,如下图所示。

SpringForm

单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringForm。另外,要自己编译和运行示例,请查阅example index

这是在 SpringForm 中创建和布置标签文本字段对的代码:

String[] labels = {"Name: ", "Fax: ", "Email: ", "Address: "};
int numPairs = labels.length;

//Create and populate the panel.
JPanel p = new JPanel(new SpringLayout());
for (int i = 0; i < numPairs; i++) {
    JLabel l = new JLabel(labels[i], JLabel.TRAILING);
    p.add(l);
    JTextField textField = new JTextField(10);
    l.setLabelFor(textField);
    p.add(textField);
}

//Lay out the panel.
SpringUtilities.makeCompactGrid(p,
                                numPairs, 2, //rows, cols
                                6, 6,        //initX, initY
                                6, 6);       //xPad, yPad

因为我们使用的是真实的布局 管理 器而不是绝对定位,所以布局 管理 器会动态响应所涉及组件的更改。例如,如果标签名称已本地化,则 Spring 布局会根据需要为第一列提供更多或更少空间的配置。如下图所示,当调整窗口大小时,灵活调整大小的组件(文本字段)会占用所有多余的空间,而标签会贴合所需。

SpringForm enlarged

我们在_中的makeCompactGrid方法的最后一个示例在SpringBox.java中显示了一些配置为单行布局的按钮。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpringBox。另外,要自己编译和运行示例,请参考example index

SpringBox

请注意,在单行的情况下,其行为几乎与BoxLayout相同。不仅布局为BoxLayout的组件可以排列它们,而且使用SpringLayout的容器的最小,首选和最大大小返回的结果与BoxLayout相同。这是对产生此布局的makeCompactGrid的调用:

//Lay out the buttons in one row and as many columns
//as necessary, with 6 pixels of padding all around.
SpringUtilities.makeCompactGrid(contentPane, 1,
                                contentPane.getComponentCount(),
                                6, 6, 6, 6);

让我们看看调整窗口大小时会发生什么。这是一个奇怪的特殊情况,值得一提,因为您可能会在第一个布局中偶然遇到它。

SpringBox resized

什么都没动!这是因为没有一个组件(按钮)或它们之间的间距被定义为可拉伸的。在这种情况下,Spring 布局会计算出父容器的最大大小,该大小等于其首选大小,这意味着父容器本身是不可拉伸的。如果 AWT 拒绝调整不可拉伸的窗口的大小,但它却不能这样做,那么可能会减少混乱。布局 管理 器在这里不能做任何明智的事情,因为所有组件都不会占用所需的空间。它不会崩溃,它什么也不做,将所有组件保持原样。

SpringLayout API

使用SpringLayout的 API 分为三个类:

SpringLayout

构造函数或方法Purpose
SpringLayout()创建一个SpringLayout实例。
SpringLayout.Constraints getConstraints(Component)获取与指定组件关联的约束(Spring 组)。
Spring getConstraint(String,Component)获取零部件边缘的 Spring。第一个参数指定边缘,并且必须是以下SpringLayout常量之一:NORTHSOUTHEASTWEST
void putConstraint(String,Component,int,String,Component)

void putConstraint(String,Component,Spring,String,Component)
用于定义两个组件的边缘之间的关系的便捷方法。前两个参数指定第一个组件及其受影响的边。最后两个参数指定第二个组件及其受影响的边。第三个参数指定确定两者之间距离的 Spring。当第三个参数为整数时,将创建一个恒定的 Spring 以在组件边缘之间提供固定的距离。

SpringLayout.Constraints

构造函数或方法Purpose
SpringLayout.Constraints()

SpringLayout.Constraints(Spring, Spring)
SpringLayout.Constraints(Spring,Spring,Spring,Spring)
创建SpringLayout.Constraints实例。前两个参数(如果存在)分别指定 X 和 YSpring。后两个参数(如果存在)分别指定了高度 Spring 和宽度 Spring。省略参数会导致相应的 Spring 为null,通常将SpringLayout替换为适当的默认值。
Spring getConstraint(String)
Spring getHeight()
Spring getWidth()
Spring getX()
Spring getY()
void setConstraint(String,Spring)
void setHeight(Spring)
void setWidth(Spring)
void setX(Spring)
void setY(Spring)
获取或设置指定的 Spring。 getConstraintsetConstraint方法的字符串 参数指定边名称,并且必须是SpringLayout常量NORTHSOUTHEASTWEST之一。

Spring

MethodPurpose
静态 Spring 常数(int)

静态 Spring 常数(int,int,int)
创建不跟踪零部件尺寸的 Spring。三参数版本创建一个 Spring,该 Spring 的最小值,首选值和最大值按该 Sequences 设置为指定值。单参数版本将创建一个 Spring,其最小值,首选值和最大值均设置为指定的整数。
尽管名称,但constant返回的 Spring 是可变的。为了制定出一个布局,可能会强制SpringLayout调整“恒定”Spring。因此,应该避免重复使用恒定 Spring,除非(1)确实希望 Spring 始终完全相同,并且(2)其他 Spring 在布局上提供一定的灵 Active。
静态 Spring sum(Spring,Spring)
静态 Spring max(Spring,Spring)
静态 Spring 减(Spring)
创建一些 math 操作的结果的 Spring。 sum方法添加两个 Spring。 max方法返回一个 Spring,该 Spring 的值始终大于或等于两个参数的值。 minus方法返回沿参数相反方向运行的 Spring。 minus方法可用于为sum方法创建参数,从而使您能够获得两个 Spring 之间的差。
int getMinimumValue()
int getPreferredValue()
int getMaximumValue()
从 Spring 中获取相应的值。对于由SpringLayout创建的自动跟踪组件的 Spring,这些方法导致调用组件的相应getXxxSize方法。
int getValue()
setValue(int)
获取或设置 Spring 的当前值。

使用 SpringLayout 的示例

下表列出了一些使用 Spring 布局的示例。

ExampleWhere DescribedNotes
SpringDemo3This page使用SpringLayout创建一排均匀间隔的自然尺寸的组件。
SpringDemo4This page重新实现 SpringDemo3 以直接使用SpringLayout.ConstraintsSpring
SpringGridThis page使用SpringLayoutmakeGridUtil 方法创建一个布局,其中所有组件的大小均相同。
SpringCompactGridThis page使用SpringLayoutmakeCompactGridUtil 方法创建一种布局,其中行中的所有组件具有相同的高度,而列中的所有组件具有相同的宽度。
SpringFormThis page使用SpringLayoutmakeCompactGrid对齐标签文本字段对。
SpringBoxThis page使用SpringLayoutmakeCompactGrid演示如何布置一行组件,以及在没有 Spring 可以生 Long 时会发生什么。
SpinnerDemo如何使用微调器使用SpringLayoutmakeCompactGrid布置标签旋转程序对的行。
TextInputDemo如何使用格式化的文本字段使用SpringLayoutmakeCompactGrid布置带标签的组件行。这些组件是文本字段,带格式的文本字段和微调框的混合。