65.9K
CodeProject 正在变化。 阅读更多。
Home

FreeLayout:新的 Java 布局

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (9投票s)

2013年12月18日

CPOL

5分钟阅读

viewsIcon

19903

downloadIcon

208

本文介绍了一种新的灵活的 Java 布局。

引言

当需要创建 GUI 时,一个主要的难题是如何将各种组件(按钮、文本框、列表等)排列到窗口中。如果窗口需要调整大小并且所有组件都必须重新定位,那么问题就变得更大了。如果在过程中需要创建另一个组件并将其放置在窗口的中间,则会更加复杂。这并非一项复杂的任务,但需要大量工作。

Java 提供了一些 布局 来帮助创建 GUI,但如果您不熟悉它们,或者您打算使用的布局太复杂,那么这项任务可能会很棘手。您可以使用一个简单的 GUI 工具,通过拖放组件到窗口中来设计您想要的布局。但我不太喜欢使用这些工具(我更习惯编写每一行代码)。

因此,在本文中,我提出了一种类,它几乎可以处理这个问题,而无需程序员做任何事情。我将其命名为 FreeLayout

使用代码

FreeLayout 只包含两个方法和一个构造函数。

提供了三个构造函数,它们都必须接收容器的尺寸(或容器本身)才能将组件定位到容器中。

public FreeLayout(Dimension size)
public FreeLayout(int width, int height)
public FreeLayout(Container container)

接下来,我们来看看负责将组件定位到容器中的方法。

public void relativeTo(Component component,
		Anchor leftAnchor,
		Anchor topAnchor,
		Anchor rightAnchor,
		Anchor bottomAnchor)

FreeLayout

FreeLayout 假定要添加的组件位置由四个 Anchor 定义,每个 Anchor 由一个组件和一个间距定义。

组件可以有两个可能的值:一个有效的组件对象(有效意味着该组件已使用 relativeTo 方法添加,在这种情况下,Anchor 指的是一个组件,如按钮、文本编辑器等),或者一个 null 对象(在这种情况下,Anchor 指的是容器的边框)。

间距是一个整数,可以是正值或负值,正值或负值会影响要添加到容器中的组件的位置。假设 Anchor 的组件不为 null。在这种情况下,无论间距值是正还是负,它都始终表示 Anchor 的组件和要添加的组件之间的分隔空间。另一方面,如果 Anchor 的组件为 null 且间距为正值,则该值表示边框和要添加的组件之间的空间。如果间距为负值(且组件仍然为 null),则该值表示要添加的组件的尺寸(对于顶部和底部锚点,此间距表示组件的高度;对于左侧和右侧锚点,此间距表示组件的宽度)。

下表总结了以上解释。

组件 (Component)
非 null null
间距 间距
components
组件间距
与容器边框
负面 间距
components
组件宽度/高度

所以,让我们看一个例子。

freeLayout.relativeTo(btn3,
		new Anchor(null, 5),
		new Anchor(btn1, 5),
		new Anchor(null, 5),
		new Anchor(null, -20));

在此示例中,我将一个组件 btn3 添加到容器中,布局如下:

  • new Anchor(null, 5)btn3 组件的左侧将参照容器的左边框,它们之间的间距为 5。
  • new Anchor(btn1, 5)btn3 组件的顶部将参照 btn1 的底部,它们之间的间距为 5。
  • new Anchor(null, 5)btn3 组件的右侧将参照容器的右侧,它们之间的间距为 5。这使得按钮的宽度从容器的一端延伸到另一端。
  • new Anchor(null, -20)btn3 组件的底部不参照任何周围组件,因为组件是 null 对象且间距为负值。在这种情况下,我定义我的 btn3 组件的高度为 20。

在下表中,我们可以看到组件可以接收的所有可能组合。

左侧组件/
顶部组件
左侧间距/
顶部间距
右侧组件/
底部组件
右侧间距/
底部间距
注释
null 负面 null 负面 无效
null 负面 null 宽度/高度 - 容器边框
null 负面 非 null 负面 宽度/高度 - 组件
null 负面 非 null 宽度/高度 - 组件
null null 负面 容器边框 - 宽度/高度
null null 容器边框 - 容器边框
null 非 null 负面 容器边框 - 组件
null 非 null 容器边框 - 组件
非 null 负面 null 负面 组件 - 宽度/高度
非 null 负面 null 组件 - 容器边框
非 null 负面 非 null 负面 组件 - 组件
非 null 负面 非 null 组件 - 组件
非 null null 负面 组件 - 宽度/高度
非 null null 组件 - 容器边框
非 null 非 null 负面 组件 - 组件
非 null 非 null 组件 - 组件

到目前为止,组件都位于我们期望的正确位置,但如果窗口是可调整大小的,那么组件必须适应容器的新尺寸。这可以通过在容器调整大小时调用的回调方法中简单地调用 invalidate 方法来完成(此方法仅更新使用 relativeTo 方法添加的组件)。

public void invalidate(int width, int height)
public void invalidate(Dimension size)
public void invalidate(Container container)

此方法只需接收容器的新尺寸(或容器本身,就像在构造函数中一样),FreeLayout 将会按照之前定义的参数定位所有组件。

示例

使用此布局非常简单,您很快就会看到。我将创建一个简单的窗口来示例如何使用 FreeLayout 类。

使此布局工作的两个关键点是:将框架布局设置为 null,并为窗格容器设置大小。我还会实现一个组件监听器来捕获 resize 事件。

以下是示例代码

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.*;
import freelayout.*;

public class Main extends JFrame implements ComponentListener {

    // Initialize the FreeLayout
    FreeLayout freeLayout = new FreeLayout(300, 300);
    
    public Main() {
        super("FreeLayout Demo");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // Set the frame layoutof to null and set the container size
        setLayout(null);
        getContentPane().setPreferredSize(new Dimension(300, 300));
        pack();
        
        JButton open = new JButton("open");
        freeLayout.relativeTo(open,
                new Anchor(null, -80),
                new Anchor(null, 5),
                new Anchor(null, 5),
                new Anchor(null, -20));
        getContentPane().add(open);
        
        JTextField textField1 = new JTextField("Text field 1");
        freeLayout.relativeTo(textField1,
                new Anchor(null, 5),
                new Anchor(null, 5),
                new Anchor(open, 5),
                new Anchor(null, -20));
        getContentPane().add(textField1);
        
        JLabel label = new JLabel("Label: ");
        freeLayout.relativeTo(label,
                new Anchor(null, 5),
                new Anchor(textField1, 5),
                new Anchor(null, -40),
                new Anchor(null, -20));
        getContentPane().add(label);
        
        JButton cancel = new JButton("cancel");
        freeLayout.relativeTo(cancel,
                new Anchor(null, -80),
                new Anchor(null, -20),
                new Anchor(null, 5),
                new Anchor(null, 5));
        getContentPane().add(cancel);
        
        JButton ok = new JButton("ok");
        freeLayout.relativeTo(ok,
                new Anchor(null, -80),
                new Anchor(null, -20),
                new Anchor(cancel, 5),
                new Anchor(null, 5));
        getContentPane().add(ok);
        
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(3,3));
        panel.add(new JButton("1"));
        panel.add(new JButton("2"));
        panel.add(new JButton("3"));
        panel.add(new JButton("4"));
        panel.add(new JButton("5"));
        panel.add(new JButton("6"));
        panel.add(new JButton("7"));
        panel.add(new JButton("8"));
        panel.add(new JButton("9"));
        freeLayout.relativeTo(panel,
                new Anchor(null, 5),
                new Anchor(null, -150),
                new Anchor(null, 5),
                new Anchor(cancel, 5));
        getContentPane().add(panel);
        
        JTextArea textArea = new JTextArea("Texta area: ");
        freeLayout.relativeTo(textArea,
                new Anchor(label, 5),
                new Anchor(textField1, 5),
                new Anchor(null, 5),
                new Anchor(panel, 5));
        getContentPane().add(textArea);
        
        // To resize the window
        addComponentListener(this);
    }
    
    public static void main(String[] args) {
        new Main().setVisible(true);
    }
    
    @Override
    public void componentHidden(ComponentEvent e) {
    }

    @Override
    public void componentMoved(ComponentEvent e) {
    }

    @Override
    public void componentResized(ComponentEvent e) {
        freeLayout.invalidate(((JFrame) (e.getComponent())).getContentPane().getSize());
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }
}

代码的结果显示在下一张图中。

FreeLayout

结论

如您所见,使用 FreeLayout 创建窗口非常简单,并且窗口调整大小时重新定位所有组件也不是问题。FreeLayout 会为您处理一切。

欢迎任何评论!

历史

  • 2013/12/17 - 原始文章。
© . All rights reserved.