使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day067 图形程序设计(二)(框架定位(框架的属性、确定合适的框架大小)、在组件中显示信息)
框架定位
JFrame类本身只包含若干个改变框架外观的方法。当然,通过继承,从JFrame的各个超类中继承了许多用于处理框架大小和位置的方法其中最重要的有下面几个:
•setLocation和setBounds方法用于设置框架的位置。
•setlconlmage用于告诉窗口系统在标题栏、任务切换窗口等位置显示哪个图标。
•setTitle用于改变标题栏的文字。
•setResizable利用一个boolean值确定框架的大小是否允许用户改变。
下图给出了 JFrame类的继承层次。
后面的API注解给出了一些最为重要的用于设置框架适当观感的方法。其中一些定义在JFrame类中,而另一些来自JFrame的各个超类。因此,可能需要查阅API文档,以便确定是否存在能够实现某个特定目的的方法。遗憾的是,在文档中查阅一个类继承的方法是一件比较令人烦恼的事情。对于子类来说,API文档只解释了覆盖的方法。例如,可以应用于JFrame类对象的toFront方法,由于它是从Window类继承而来的,所以JFrame文档没有对它进行解释。如果认为应该有一个能够完成某项操作的方法,而在处理的类文档中又没有解释,就应该查看这个类的超类API文档。每页API上面都有一个对超类的超链接,继承方法被列在新方法和覆盖方法的汇总下面。
正像API注解中所说的,对Component类(是所有GUI对象的祖先)和Window类(是Frame类的超类)需要仔细地研究一下,从中找到缩放和改变框架的方法。例如,在Component类中的setLocation方法是重定位组件的一个方法。如果调用
setLocation(x, y)
则窗口将放置在左上角水平x像素,垂直y像素的位置,坐标(0,0)位于屏幕的左上角。同样地,Component中的setBounds方法可以实现一步重定位组件(特别是JFrame)大小和位置的操作,例如:
setBounds(x, y, width, height)
可以让窗口系统控制窗口的位置,如果在显示窗口之前调用
setLocationByPlatform(true);
窗口系统会选用窗口的位置(而不是大小),通常是距最后一个显示窗口很少偏移量的位置。
1.框架的属性
组件类的很多方法是以获取 / 设置方法对形式出现的,例如,Frame类的下列方法:
public String getTitle()
public void setTitle(String title)
这样的一个获取/设置方法对被称为一种属性。属性包含属性名和类型。将get或set之后的第一个字母改为小写字母就可以得到相应的属性名。例如,Frame类有一个名为title且类型为String的属性。
从概念上讲,title是框架的一个属性。当设置这个属性时,希望这个标题能够改变用户屏幕上的显示。当获取这个属性时,希望能够返回已经设置的属性值。
我们并不清楚(也不关心)Frame类是如何实现这个属性的。或许只是简单的利用对等框架存储标题。或许有一个实例域:
private String title;//not required for property
如果类没有匹配的实例域,我们将不清楚(也不关心)如何实现获取和设置方法。或许只是读、写实例域,或许还执行了很多其他的操作。例如,当标题发生变化时,通知给窗口系统。
针对get/set约定有一个例外:对于类型为boolean的属性,获取方法由is开头。例如,下面两个方法定义了locationByPlatform属性:
public boolean islocationByPIatforn()
public void setLocationByPIatforra(boolean b)
2.确定合适的框架大小
要记住:如果没有明确地指定框架的大小,所有框架的默认值为0x0像素。为了让示例程序尽可能地简单,这里将框架的大小重置为大多数情况下都可以接受的显示尺寸。然而,对于专业应用程序来说,应该检查屏幕的分辨率,并根据其分辨率编写代码重置框架的大小,如在膝上型电脑的屏幕上,正常显示的窗口在高分辨率屏幕上可能会变成一张邮票的大小。
为了得到屏幕的大小,需要按照下列步骤操作。调用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;
下面,将框架大小设定为上面取值的 50%,然后,告知窗口系统定位框架:
setSize(screenWidth /2, screenHeight/2);
setLocationByPlatfori(true);
另外,还提供一个图标。由于图像的描述与系统有关,所以需要再次使用工具箱加载图像。然后,将这个图像设置为框架的图标。
Image img = new Inagelcon("icon.gif").getlmage();
setlconlnage(img);
于不同的操作系统,所看到的图标显示位置有可能不同。例如,在Windows中,图标显示在窗口的左上角,按下ALT+TAB,可以在活动任务的列表中看到相应程序的图标。
下面是完整的程序。当运行程序时,请注意看“CoreJava”图标。
下面是为了处理框架给予的一些提示:
•如果框架中只包含标准的组件,如按钮和文本框,那么可以通过调用pack方法设置框架大小。框架将被设置为刚好能够放置所有组件的大小。在通常情况下,将程序的主框架尺寸设置为最大。可以通过调用下列方法将框架设置为最大。
frame.setExtendedState(Frame.MAXIMIZEDJOTH):
•牢记用户定位应用程序的框架位置、重置框架大小,并且在应用程序再次启动时恢复这些内容是一个不错的想法。
•GraphicsDevice类还允许以全屏模式执行应用。
/**
*@author zzehao
*/
import java.awt.*;
import javax.swing.*;
public class SizedFrameTest
{
public static void main(String[] agrs)
{
EventQueue.invokeLater(() ->
{
JFrame frame = new SizedFrame();
frame.setTitle("SizedFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
class SizedFrame extends JFrame
{
public SizedFrame()
{
//get screen dimensions
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
//set frame width,height and let platform pick screen location
setSize(screenWidth / 2,screenHeight / 2);
setLocationByPlatform(true);
//set frame icon
Image img = new ImageIcon("icon.gif").getImage();
setIconImage(img);
}
}
运行的结果:
3.在组件中显示信息
下面学习如何在框架内显示信息。例如,我们不再像之前那样,采用文本方式将“NotaHello,Worldprogram”显示在控制台窗口中,而是在框架中显示这个消息。
可以将消息字符串直接绘制在框架中,但这并不是一种好的编程习惯。在Java中,框架被设计为放置组件的容器,可以将菜单栏和其他的用户界面元素放置在其中。在通常情况下,应该在另一组件上绘制信息,并将这个组件添加到框架中。
JFrame的结构相当复杂。在下图中给出了JFrame的结构。可以看到,在JFrame中有四层面板。其中的根面板、层级面板和玻璃面板人们并不太关心;它们是用来组织菜单栏和内容窗格以及实现观感的。Swing程序员最关心的是内容窗格(content pane)。在设计框架的时候,要使用下列代码将所有的组件添加到内容窗格中:
Container contentPane=frame.getContentPane();
Component c=. . .;
contentPane.add(c);
在JavaSE1.4及以前的版本中,JFrame类中的add方法抛出了一个异常信息“Do not use JFrame.add().Use JFrame.getContentPaneQ.add instead”。如今,JFrame.add方法不再显示这些提示信息,只是简单地调用内容窗格的add,因此,可以直接调用
frame.add(c);
在这里,打算将一个绘制消息的组件添加到框架中。绘制一个组件,需要定义一个扩展JComponent的类,并覆盖其中的paintComponent方法。
paintComponent方法有一个Graphics类型的参数,这个参数保存着用于绘制图像和文本的设置,例如,设置的字体或当前的颜色。在Java中,所有的绘制都必须使用Graphics对其中包含了绘制图案、图像和文本的方法。
下列代码给出了如何创建一个能够进行绘制的组件:
class MyComponent extends JComponent
{
public void paintComponent(Graphics g)
{
code for drawing
}
}
无论何种原因,只要窗口需要重新绘图,事件处理器就会通告组件,从而引发执行所有组件的paintComponent方法。
一定不要自己调用paintComponent方法。在应用程序需要重新绘图的时候,这个方法将被自动地调用,不要人为地干预这个自动的处理过程。
何种类别的动作会触发这个自动响应过程呢?例如,在用户扩大窗口或极小化窗口,然后又恢复窗口的大小时会引发重新绘图。如果用户弹出了另外一个窗口,并且这个窗口覆盖了一个已经存在的窗口,使得覆盖的窗口不可见,则此时被覆盖的应用程序窗口被破坏,需要重新绘制(图形系统不保存下面的像素)。当然,窗口第一次显示时,需要处理一些代码,主要包含确定绘制最初元素的方式以及位置。
如果需要强制刷新屏幕,就需要调用repaint方法,而不是paintComponent方法。它将引发采用相应配置的Graphics对象调用所有组件的paintComponent方法。
从上述代码片段中可以看到,paintComponent方法只有一个Graphics类型的参数。对于屏幕显示来说,Graphics对象的度量单位是像素。坐标(0,0)指出所绘制组件表面的左上角。显示文本是一种特殊的绘图。在Graphics类中有一个drawstring方法,调用的语法格式为:
g.drawString(text, x, y)
在这里,打算在原始窗口大约水平1/4,垂直1/2的位置显示字符串“NotaHello,Worldprogram"o现在,尽管不知道应该如何度量这个字符串的大小,但可以将字符串的开始位置定义在坐标(75,100)。这就意味着字符串中的第一个字符位于从左向右75个像素,从上向下100个像素的位置。因此,paintComponent方法的书写内容如下所示:
public class NotHel1olulorldComponent extends JComponent
{
public static final int MESSACE_X = 75;
public static final int MESSACE_Y = 100;
public void paintComponent(Graphics g)
{
g.drawString("Not a Hello World program", MESSACE_X, MESSACE_Y);
}
...
}
最后,组件要告诉用户它应该有多大。覆盖getPreferredSize方法,返回一个有首选宽度和高度的Dimension类对象:
public class NotHel1oWorldComponent extends JComponent
{
private static final int DEFAULT.WIDTH = 300;
private static final int DEFAULT.HEIGHT = 200;
...
public Dimension getPreferredSize(){ return new Dimension(DEFAULT_WIDTH, DEFAULT_HEICHT); }
}
在框架中填入一个或多个组件时,如果你只想使用它们的首选大小,可以调用pack方法而不是setSize方法:
class NotHelloWoridFrame extends JFrame
{
public NotHelloWoridFrame()
{
add(new NotHel1oWorldComponent());
pack();
}
}
下面是完整的代码:
/**
*@author zzehao
*/
import java.awt.*;
import javax.swing.*;
public class NotHelloWorld
{
public static void main(String[] agrs)
{
EventQueue.invokeLater(() ->
{
JFrame frame = new NotHelloWorldFrame();
frame.setTitle("NotHelloWorld");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
add(new NotHelloWorldComponent());
pack();
}
}
class NotHelloWorldComponent extends JComponent
{
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public void paintComponent(Graphics g)
{
g.drawString("Not a Hello,world program",MESSAGE_X,MESSAGE_Y);
}
public Dimension getPreferredSize()
{
return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
}
}
运行的结果:
有些程序员更喜欢扩展JPanel,而不是JComponent。JPanel是一个可以包含其他组件的容器(container),但同样也可以在其上面进行绘制。有一点不同之处是,面板不透明,这意味着需要在面板的边界内绘制所有的像素。最容易实现的方法是,在每个面板子类的paintComponent方法中调用super.paintComponent来用背景色绘制面板:
class NotHel1oWorldPanel extendsJPanel
{
public void paintComponent(Craphics g)
{
super.paintComponent(g);
code for drawing
}
}