Using Top-Level Containers(使用最上层容器)
Swing提供了三个主要的有用的最上层容器类:JFrame、JDialog和JApplet。在使用这些类时,你应该牢记如下事实:
l 若想在屏幕上显示,每个GUI组件必须是一个包含层次的一部分。一个包含层次是一个以最上层(Top-Level)容器为根的组件树形结构。
l 每个GUI组件只能被包含一次。假如一个组件已经在一个容器中,并且你尝试将其添加到另一个容器,那么该组件就会从第一个容器中删除并添加到第二个容器中。
l 每个最上层容器拥有一个内容面板(content pane),大致来说,包含(直接或间接)在那个最上层容器中的可见组件。
l 你可以有选择地将一个菜单条(Menu bar)添加到最上层容器中。按照惯例,菜单条被放置在最上层容器中,但是在内容面板之外。有些外观(look and feel),例如Mac OS,提供给你将菜单条放到另外一个更合适的位置的选择,例如在屏幕的最顶部。
Note:尽管JInternalFrame模仿JFrame,但内部窗口类实际上并不是最上层容器。
这里有一幅由一个应用程序产生的图片。这个窗口包含一个绿色的菜单条(没有菜单),以及一个大的、黄色的标签在窗口的内容面板中。
完整的源程序如下:
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.BorderLayout;
public class TopLevelDemo {
/**
* 创建GUI并显示。为了线程安全,这个方法应该
* 在事件派发线程中调用。
* */
private static void createAndShowGUI() {
// 创建并设置窗口
JFrame frame = new JFrame("TopLevelDemo");
// 点击关闭退出程序
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 创建菜单条,使它有一个绿色的背景。
JMenuBar greenMenuBar = new JMenuBar();
// 设置其不透明
greenMenuBar.setOpaque(true);
// 设置其背景色
greenMenuBar.setBackground(new Color(154, 165, 127));
// 设置组件的首选大小
greenMenuBar.setPreferredSize(new Dimension(200, 20));
// 创建一个黄色的标签放以到内容面板中
JLabel yellowLabel = new JLabel();
// 设置其不透明
yellowLabel.setOpaque(true);
// 设置其背景色为黄色
yellowLabel.setBackground(new Color(248, 213, 131));
// 设置组件的首选大小
yellowLabel.setPreferredSize(new Dimension(200, 180));
// 设置菜单条,并将标签放到内容面板中
frame.setJMenuBar(greenMenuBar);
// 窗口内容面板默认布局为BorderLayout,将标签添加到面板中央
frame.getContentPane().add(yellowLabel, BorderLayout.CENTER);
// 显示窗口
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// 为事件派发线程增加一个任务:
// 创建并显示这个应用程序的GUI(图像用户界面)
//
// 在事件派发线程中所有挂起的事件都被处理后才会调用如下方法,
// 常用于更新界面。
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
尽管这个例子在一个单一的应用程序中仅使用了一个窗口,但是原理对于JApplet和JDialog
同样适用。
这个GUI实例的包含层次图如下:
上图中的省略号表示我们将其中的一些细节忽略了。我们待会将揭示这些忽略的细节。本
部分将要讨论的主题如下:
l 最上层容器和包含层次
l 添加组件到内容面板
l 添加一个菜单条
l 根面板(忽略的细节)
最上层容器和包含层次
每个使用Swing组件的程序都拥有至少一个最上层容器。这个最上层容器是(组件)包含层次的根——这个层次,包含所有的Swing组件,出现在顶层容器中。
作为一条准则,一个以Swing最为GUI基础的独立的应用程序都拥有一个以JFrame作为根的包含层次。例如,假如一个应用程序有一个主窗口和两个对话框的话,那么这个程序就三个包含层次,也就是三个最上层容器。一个以JFrame作为根的包含层次,另外的每个都有以JDialog作为根的包含层次。
添加组件到内容面板
下面有一段前面的那个例子所使用的代码用来获得一个窗口的内容面板,并将黄色的标签添加到其中:
frame.getContentPane().add(yellowLabel, BorderLayout.CENTER);
正如代码所示,你可以通过调用getContentPane()找到最上层容器的内容面板。默认的内容面板是一个简单的从JComponent继承的中间容器,并且使用BorderLayout作为其默认的布局管理器。
自定义内容面板很简单——例如,可以设置其布局管理器或者添加边框。然而,这里有一个小注意。getContentPane方法返回的是一个Container对象,不是一个JComponent对象。这说明假如你想利用内容面板的JComponent特性,你需要或是强制类型转换一下它的返回值,抑或是创建你自己的组件来代替内容面板。我们的例子采用了后一种方法,是由于它产生的代码干净一些。另外一种方法,我们有时会采用它来简化添加一个自定义的组件到内容面板中,并完全覆盖之前的内容面板。
意识到JPanel默认的布局管理器是FlowLayout;你可能会想更改它。
将一个组件设置为内容面板,可以使用最上层容器的setContentPane方法。例如:
// 创建一个面板,并向其添加一些组件
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(someBorder); // 设置边框
// 添加到中间区域
contentPane.add(someComponent, BorderLayout.CENTER);
// 组件出现在最后一行布局内容之后
contentPane.add(anotherComponent, BorderLayout.PAGE_END);
// 将新建的面板设置为内容面板
topLevelContainer.setContentPane(contentPane);
Note:方便起见,add方法及其一些变体,remove和setLayout已经被重载了以直接对contentPane起作用。这意味着你可以这样写:
frame.add(child);
这样child将会被添加进contentPane。
意识到只有这三个方法可以这样做。这说明了getLayout()将不会返回使用setLayout()设置的布局管理器。
添加菜单条
理论上,所有的最上层容器都可以拥有菜单条。然而,在实际应用中,菜单条通常只在frames和applets中出现。为了将菜单条添加进最上层容器,我们可以先创建一个JMenuBar对象,为其添加菜单项,然后调用setMenuBar方法。前面的TopLevelDemo实例用如下代码将菜单条添加进它的窗口:
frame.setJMenuBar(greenMenuBar);
根多有关事项菜单及菜单条的信息,见How to Use Menus。
根面板
每个最上层容器都基于一个称为根面板(root pane)的容器。根面板管理内容面板和菜单条,及其他的一些容器。在使用Swing组件的时候,你一般不需要知道root pane。然而,假如你曾想拦截鼠标的点击或者在多重组件上绘图,那么你应该就要很熟悉根面板了。
这里有一个根面板提供给一个窗口(或其他另外的最上层容器)的组件列表:
我们已经告诉过你有关内容面板和可选的菜单条。根面板添加的另外两个组件是层次面板(Layered pane)和玻璃面板(glass pane)。层次面板包含菜单条和内容面板,并且支持组件的Z坐标。玻璃面板常用来捕获发生在最上层容器上的输入事件,也可以用来在多个组件上绘图。
可以参见How to Use Root Panes以获得更多细节。