框架定位
JFrame类本身只包含若干个改变框架外观的方法。然而,通过继承从JFrame的各个超类中继承了许多用于处理框架大小和位置的方法。其中最重要的有下面几个:
• dispose方法用于关闭窗口,并回收创建窗口所使用的全部系统资源。
• setIconImage方法用于将窗口极小化时的图标设置为Image对象(在Java中,通常被称为图标化)。
• setTitle方法用于改变标题栏中的文本。
• setResizable方法利用一个boolean值确定框架的大小是否允许用户改变。
图7-5给出了JFrame类的继承层次结构。
如同API注解中所说的那样,对Component类(是所有GUI对象的祖先)和Window类(是Frame类的超类)需要仔细地研究一下,从中找到缩放和改造框架的方法。
例如,在Component类中的setLocation方法是重定位组件的一个方法。如果调用setLocation(x, y)
窗口将放置在左上角水平x像素,垂直y像素的位置,坐标(0,0)位于屏幕的左上角。同样地,Component中的setBounds方法可以实现一步重定位组件(特别是JFrame)大小和位置的操作,例如:
setBounds(x, y, width, height)
注意:对于框架来说,setLocation和setBounds中的坐标均相对于整个屏幕。在第9章中将会看到,在容器中包含的其他组件,其坐标相对于容器。
请牢记,如果没有明确地指定框架的大小,所有框架的默认值为0×0像素。为了让示例程序尽可能地简单,我们将框架的大小重置为大多数情况下都可以接受的显示尺寸。然而,对于专业应用程序来说,应该检查用户屏幕的分辨率,并根据其分辨率编写代码重置框架的大小,如在膝上型电脑的屏幕上,正常显示的窗口在高分辨率屏幕上可能会变成一张邮票的大小。稍后我们会看到,可以获得用户系统的基于像素的屏幕分辨率信息,然后利用这些信息计算程序中最佳的窗口大小。
提示:本节API注解给出了一些非常重要的用于设置框架适当观感的方法。其中一些定义在JFrame类中,而另一些来自于JFrame的各个超类。因此,可能需要查找API文档,以便确定是否存在能够实现某个特定目的的方法。遗憾的是,查找JDK文档是一件比较令人烦恼的事情。对于子类来说,API文档只解释了覆盖的方法。例如,可以应用于JFrame类型的对象的toFront方法,由于它是从Window类继承而来的,所以JFrame文档没有对它进行解释。如果认为应该有一个能够完成某项操作的方法,而在所处理的类的文档中又没有解释,就应该查看这个类的超类API文档。每页API上面都有一个对超类的超链接,继承方法被列在新方法和覆盖方法的汇总下面。
为了说明窗口的操作,在本节结尾,给出一个示例程序,它将一个可关闭框架的设置为:
• 其大小是整个屏幕的四分之一。
• 位于屏幕的中央。
例如,如果屏幕的分辨率为800×600像素,那么框架的大小为400×300像素,它的左上角应该位于(200,150)。
为了得到屏幕的大小,需要按照下列步骤操作。调用Toolkit类的静态方法getDefaultToolkit得到一个Toolkit对象。(Toolkit类包含很多与本地窗口系统进行交互的方法)。然后,调用getScreenSize方法,该方法以Dimension对象的形式返回屏幕的大小。Dimension对象同时用公有实例变量width和height保存屏幕的宽度和高度。下面是相关的代码:
Toolkit kit = Toolkit.getDefaultToolkit( );Dimension screenSize = kit.getScreenSize( );int screenWidth = screenSize.width;int screenHeight = screenSize.height;
另外,还提供了一个图标。由于图像的描述与系统有关,所以需要再次使用工具箱来加载图像。然后,将这个图像设置为框架的图标。
Image img = kit.getImage("icon.gif");setIconImage(img);
对于不同的操作系统,所看到的图标显示位置有可能不同。例如,在Windows中,图标显示在窗口的左上角,按下ALT+TAB,可以在活动任务的列表中看到相应程序的图标。
例7-2是完整的程序。当运行程序时,请注意看“Core Java”图标。
提示:经常会将程序的主框架设置为最大尺寸。在JDK 1.4版本中,可以调用frame.setExtendedState(Frame.MAXIMIZED_BOTH);
将框架设置为最大。
注意:如果编写一个利用多个显示屏幕的应用程序,就应该使用GrapicsEnvironment和GraphicsDevice类获得显示屏幕的大小。在JDK 1.4中,GraphicsDevice类允许在全屏幕模式下运行应用程序。
例7-2 CenteredFrameTest.java
java.awt.Component 1.0
• boolean isVisible( )
检查组件是否设置为可见。组件最初是可见的,但JFrame这样的顶层组件除外。
• void setVisible(boolean b)
根据b是true或false显示或隐藏组件。
• boolean isShowing( )
检查该组件是否显示在屏幕中。如果是,它必须是可见的,且包含在一个正在显示的容器中。
• boolean isEnabled( )
检查组件是否被激活。被激活的组件可以接受键盘输入。组件最初是被激活的。
• void setEnabled(boolean b)
激活或禁用组件。
• Point getLocation( ) 1.1
返回该组件左上角的位置,这是相对于外围容器左上角的位置。(Point对象p封装了坐标x和y,访问的格式为p.x和p.y。)
• Point getLocationOnScreen( ) 1.1
返回该组件左上角的位置,这个位置用屏幕坐标表示。
• void setBounds(int x, int y, int width, int height) 1.1
移动并重置组件的大小。参数x和y是给定的左上角位置,参数width和height是给定的新尺寸。
• void setLocation(int x, int y) 1.1
• void setLocation(Point p) 1.1
将组件移到一个新的位置上。如果该组件不是顶层组件,x和y坐标(或者p.x和p.y)是容器
坐标,否则该组件是顶层组件(如JFrame),x和y是屏幕坐标。
• Dimension getSize( ) 1.1
返回该组件当前的尺寸。
• void setSize(int width, int height) 1.1
• void setSize(Dimension d) 1.1
使用给定的宽度和高度,重置组件的大小。
java.awt.Window 1.0
• void toFront( )
将该窗口显示在其他窗口前面。
• void toBack( )
将该窗口移到桌面窗口栈的后面,并重新排列所有的可见窗口。
java.awt.Frame 1.0
• void setResizable(boolean b)
决定用户是否可以重置框架的大小。
• void setTitle(String s)
将框架标题栏中的文字设置为字符串s。
• void setIconImage(Image image)
参数:image
框架显示图标的图像
• void setUndecorated(boolean b) 1.4
如果b为true,取消框架装饰。
• boolean isUndecorated( ) 1.4
如果框架没有装饰,返回true。
• int getExtendedState( ) 1.4
• void setExtendedState(int state) 1.4
获得或设置窗口状态。状态是下列值之一
• Frame.NORMAL• Frame.ICONIFIED• Frame.MAXIMIZED_HORIZ• Frame.MAXIMIZED_VERT• Frame.MAXIMIZED_BOTH
java.awt.Toolkit 1.0
• static Toolkit getDefaultToolkit( )
返回默认的工具箱。
• Dimension getScreenSize( )
返回用户屏幕的尺寸。
• Image getImage(String filename)
加载文件名为filename的图像。
在面板中显示信息
在本节中,将论述如何在框架内显示信息。例如,我们不再像第3章那样,采用文本方式将“Not a Hello, Worldprogram”显示在控制台窗口中,而是在框架中显示该消息,如图7-6所示。
可以将消息字符串直接绘制在框架中,但这种做法并不是一种良好的编程习惯。在Java中,框架被设计为放置组件的容器,可以将菜单栏和其他的用户界面元素放置在其中。在通常情况下,应该在一个称为面板(panel)的组件上绘制信息,并将这个面板添加到框架中。
JFrame的结构相当复杂。在图7-7中给出了JFrame的结构。可以看到,在JFrame中有四层面板。
其中的根面板、层级面板和玻璃面板我们并不太关心;它们是用来组织菜单栏和内容窗格以及实现观感的。Swing程序员最关心的是内容窗格(content pane)。在设计框架的时候,要使用下列代码将所有的组件添加到内容窗格中:
Container contentPane = frame.getContentPane( );
Component c = . . .;
contentPane.add(c);
在JDK 1.4及以前的版本中,JFrame类中的add方法定义为抛出一个异常消息“Donot use JFrame.add( ).Use JFrame.getContentPane( ) .add( ) instead”。在JDK 5.0中,JFrame.add方法不再显示这个异常信息了,而是调用内容窗格中的add。
因此,在JDK 5.0中,可以直接调用:
frame.add(c);
在这里,我们打算将一个绘制消息的面板添加到框架中。面板用JPanel类实现。
这种用户界面元素具有两个很有用的特性:
• 包含一个可以用于绘图的表面。
• 本身也是一个容器。
因此,在面板中还可以放置像按钮、滑块等这样的用户界面组件。
为了使面板更加具有实际应用价值,应该通过继承创建一个新类,然后覆盖或添加一些方法,来获得所需的附加功能。
尤其是,为了能够在面板上进行绘图,需要
• 定义一个扩展于JPanel的类;
• 在这个类中,覆盖paintComponent方法。
实际上,paintComponent方法定义在JComponent类中,这个类是所有非窗口Swing组件的超类。
该方法有一个Graphics类型的参数。Graphics对象保存着用于绘制图像和文本的设置,例如,设置的字体或当前的颜色。在Java中,所有的绘图都必须通过Graphics对象,其中包含绘制图案、图像和文本的方法。
注意:Grpahics参数与Windows中的设备环境或X11程序设计中的图形环境基本类似。
下面的代码给出了如何创建一个能够进行绘图的面板:
不管何种原因,只要窗口需要重新绘图,事件处理器就会通告组件,从而引发执行所有组件的painComponent方法。
一定不要自己调用paintComponent方法。在应用程序需要重新绘图的时候,这个方法将被自动地调用,不要人为地干扰这个自动的处理过程。
何种类别的动作会触发这个自动响应过程呢?例如,在用户扩大窗口或极小化窗口,然后又恢复窗口的大小时会引发重新绘图。如果用户弹出了另外一个窗口,并且这个窗口覆盖了一个已经存在的窗口,使得覆盖的窗口不可见,这时被覆盖的应用程序窗口被破坏,需要重新绘制。(图形系统不保存下面的像素。)当然,窗口第一次显示时,需要处理一些代码,主要包含确定绘制最初元素的方式以及位置。
提示:如果需要强制刷新屏幕,就需要调用repaint方法,而不是paintComponent方法。它将引发采用相应配置的Graphics对象调用所有组件的paintComponent方法。
从上述代码片段中可以看到,paintComponent方法只有一个Graphics类型的参数。Graphics对象对屏幕显示的度量单位是像素。坐标(0,0)指出所绘制组件表面的左上角。
显示文本是一种特殊的绘图。在Graphics类中有一个drawString方法,调用它的语法格式为:
g.drawString(text, x, y)
在这里,我们打算在原始窗口大约水平1/4,垂直1/2的位置显示字符串“Not a Hello, Worldprogram”。尽管现在我们还不知道应该如何度量这个字符串的大小,但可以将字符串的开始位置定义在坐标(75,100)。这就意味着字符串中的第一个字符位于从左向右75个像素,从上向下100个像素的位置。(实际上,文本的基线位于像素100的位置,有关文本的度量方式将在稍后阐述。)因此,paintComponent方法的书写内容如下所示:
然而,这个paintComponent方法并不完整。NotHelloWorldPanel类扩展了JPanel类,在填充面板的背景颜色时,JPanel类有自己的处理方式。为了确保超类完成自己那部分工作,必须在绘制自己的内容之前调用super.paintComponent方法。
例7-3给出了完整的代码。如果使用的是JDK 1.4或更低的的版本,需要将add(panel) 改为
getContentPane( ).add(panel)。
例7-3 NotHelloWorld.java
javax.swing.JFrame 1.2
• Container getContentPane( )
返回该JFrame的内容窗格对象。
• void add(Component c)
将一个给定的组件添加到该框架的内容窗格中。(在JDK 5.0以前的版本中,这个方法将抛
出一个异常。)
java.awt.component 1.0
• void repaint( )
“尽可能快地”重新绘制组件。
• public void repaint(int x, int y, int width, int height)
“尽可能快地”重新绘制组件的一部分。
javax.swing.JComponent 1.2
• void paintComponent(Graphics g)
覆盖这个方法来描述应该如何绘制自己的组件。