顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架。AWT中有一个Frame类,用于描述顶层窗口。这个类的Swing版本名为JFrame,它扩展于Frame类。
JFrame示例:
import javax.swing.*;
public class SimpleFrameTest
{
public static void main(String[] args)
{
SimpleFrame frame = new SimpleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class SimpleFrame extends JFrame
{
public SimpleFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
默认情况下,框架的大小为0*0,这种框架没有意义。需要定义一个子类,使用构造器修改框架大小。
在每个Swing程序中,有两点技术需要强调:
首先,所有的Swing组件必须由事件调度进程(event dispatch thread)进行配置,线程将鼠标点击和键盘敲击控制转移到用户接口组件。
下面的代码片段是事件调度线程中的执行代码:
EventQueue.invokeLater(new Runnable()
{
public void run()
{
statements
}
});
接下来,定义一个用户关闭这个框架时的响应动作。对于这个程序而言,只让程序简单地退出即可。选择这个响应动作的语句是:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
在包含多个框架的程序中,不能在用户关闭其中的一个框架时就让程序退出。默认情况下,用户关闭窗口时只是将框架隐藏起来,而程序并没有终止。
框架起初是不可见的,程序员可以在框架第一次显示之前往其中添加组件。main方法调用框架的setVisible方法以显示框架。
在初始化语句结束后,main方法退出。此时并没有终止程序,终止的只是主线程。事件调度线程保持程序处于激活状态。
框架定位
public String getTitle()
public void setTitle(String title)
针对get/set约定有一个例外:对于类型为boolean属性,获取类方法由is开头,例如:
public boolean isLocationByPlatform()
public void setLocationByPlatform(boolean b)
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
下面,将框架大小设定为上面取值的50%,然后告知系统定位框架:
setSize(screenWidth / 2, screenHeight / 2);
setLocationByPlatform(true);
另外,为框架设置一个图标:
Image img = kit.getImage("icon.gif");
setIconImage(img);
提示:
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
2. 牢记用户定位应用程序的框架位置、重置框架大小,并且在应用程序再次启动时恢复这些内容是不错的想法。
Container contentPane = frame.getContentPane();
Component c = ...;
contentPane.add(c);
绘制一个组件,需要定义一个扩展JComponent的类,并覆盖其中的paintComponent方法。
class MyComponent extends JComponent
{
public void paintComponent(Graphics g)
{
code for drawing
}
}
只要窗口需要重新绘图,事件处理器就会通告组件,从而引发执行所有组建的paintComponent方法。
g.drawString(x, y)
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
}
Java2D库包含描述直线、矩形、椭圆的类(全部实现了Shape接口):
Rectangle2D rect = ...;
draw(rect);
为了避免后缀和类型转换带来的麻烦,2D库设计者为每个图形类提供两个版本(float和double):
Rectangle2D.Float floatRect = new Rectangle2D.Float(10.0F, 25.0F, 22.5F, 20.0F);
Rectangle2D.Double doubleRect = new Rectangle2D.Double(10.0, 25.0, 22.5, 20.0);
Rectangle2D方法的参数和返回值均为double类型。例如,即使Rectangle2D.Float对象存储float类型的宽度,getWidth方法也返回一个double值。
Rectangle2D rect = new Rectangle2D.Double(px, py, qx - px, qy - py); // ERROR
如果p不是左上角,矩形就为空。
Rectangle2D rect = new Rectangle2D.Double();
rect.setFrameFromDiagonal(px, py, qx, qy);
或者,如果已知顶点分别用Point2D类型的两个对象p和q表示,就应该这样调用:
rect.setFrameFromDiagonal(p, q);
构造椭圆时,通常可以知道椭圆的中心,宽和高。setFrameFromCenter方法使用中心点,但仍要给出四个顶点中的一个。
Ellipse2D ellipse = new Ellipse2D.Double(centerX - width / 2, centerY - height / 2, width, height);
构造直线,需提供起点和终点,这两个点既可以使用Point2D对象表示,也可以用一对数值表示:
Line2D line = new Line2D.Double(start, end);
Line2D line = new Line2D.Double(startX, startY, endX, endY);
g2.setPaint(Color.RED);
g2.drawString("Warning!", 100, 100);
将draw替换为fill就可以用一种颜色填充一个封闭图形,例如:
Rectangle2D rect = ...;
g2.setPaint(Color.RED);
g2.fill(rect); // fills rect with red color
在java.awt.Color类中提供了13个预定义的常量,表示13种标准颜色:
Color(int redness, int greenness, int blueness)
例:
g2.setPaint(new Color(0, 128, 128)); // a dull blue-green
g2.drawString("Welcome!", 75, 125);
注:如果使用Graphics对象,而不是Graphics2D对象,就需要使用setColor方法设置颜色。
MyComponent p = new MyComponent();
p.setBackground(Color.PINK);
另外,还有一个setForeground方法,用来设定在组件上进行绘制时使用的默认颜色。
p.setBackground(SystemColor.window)
它将把面板的背景颜色设定为用户界面上所有窗口使用的默认颜色。
desktop | 桌面背景颜色 | textText | 文本前景颜色 |
activeCaption | 标题背景颜色 | textInactiveText | 非活动组件文本颜色 |
activeCaptionText | 标题文本颜色 | textHighlight | 高亮度文本背景颜色 |
activeCaptionBorder | 标题文本边框颜色 | textHighlightText | 高亮度文本文本颜色 |
inactiveCaption | 非活动标题背景颜色 | control | 组件背景颜色 |
inactiveCaptionText | 非活动标题文本颜色 | controlText | 组件文本颜色 |
inactiveCaptionBorder | 非活动标题文本边框颜色 | controlLtHighlight | 组件浅高亮度颜色 |
Window | 窗口背景 | controlHighlight | 组件高亮度颜色 |
windowBorder | 窗口边框颜色 | controlShadow | 组件阴影颜色 |
windowText | 窗口内文本颜色 | controlDkShadow | 组件暗阴影颜色 |
menu | 菜单背景颜色 | scrollbar | 滚动条背景颜色 |
menuText | 菜单文本颜色 | info | 帮助区文本的颜色 |
text | 文字背景颜色 | infoText | 帮助区的文本颜色 |
import java.awt.*;
public class ListFonts
{
public static void main(String[] args)
{
String[] fontNames = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
for (String fontName : fontNames)
System.out.println(fontName);
}
}
为了创建一个公共基准,AWT定义了五个逻辑(logical)字体名:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
第三个参数是以点数目计算字体大小,每英寸包含72个点。
URL url = new URL("http://www.fonts.com/Wingbats.ttf");
InputStream in = url.openStream();
Font f1 = Font.createFont(font, TRUETYPE_FONT, in);
上面定义的字体为常规字体,大小为1.可以使用deriveFont方法得到希望大小的字体:
Font f = f1.deriveFont(14.0F);
注:deriveFont有两个重载版本,除了上述版本,还有一个版本接受一个int参数,用来设置字体风格,所有deriveFont(14)设置的是字体风格,不是大小。
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
g2.setFont(sansbold14);
String message = "Hello, world!";
g2.drawString(message, 75, 100);
接下来,将字符串绘制在面板中央,而不是任意位置。因此,需要知道字符串占据的宽和高的像素数量。这两个值取决于三个因素:
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
double stringWidth = bounds.getWidth();
double stringHeight = bounds.getHeight();
double ascent = -bounds.getY();
如果需要知道下坡度或行间距,可以使用Font类的getLineMetrics方法。这个方法返回一个LineMetrics类对象,获得下坡度和行间距的方法是:
LineMetrics metrics = f.getLineMetrics(message, context);
float descent = metrics.getDescent();
float leading = metrics.getLeading();
下面代码使用了所有这些信息,将字符串显示在包围它的组件中央:
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
// (x, y) = top left corner of text
double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;
// add ascent to y to reach the baseline
double ascent = -bounds.getY();
double baseY = y + ascent;
g2.drawString(message, (int)x, (int)baseY);
FontRenderContext context = getFontMetrics(f).getFontRenderContext();
String filename = "...";
Image image = ImageIO.read(new File(filename));
否则,应提供URL:
String urlname = "...";
Image image = ImageIO.read(new URL(urlname));
如果图像不可用,read方法将抛出一个IOException。
public void paintComponent(Graphics g)
{
...
g.drawImage(image, x, y, null);
}