假设你要在 Swing 中使用 Graphics 对象画一些字符。你的程序需要显示两行字符。程序调用 Graphics.drawString 方法画第一行,然后,在调用一次,画第二行。 DrawString 方法需要你为字符指定起始位置 X, Y 。对于第二行,你假设 Y 增加 8 将实现这一功能。就是,你假设字符的高度是 8 。例如,如果第一行的起始位置是 100,100 ,那么,第二行的起始位置是 100,108 。其代码段如下:
package EXP;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FmDemo1 {
public static void main(String args[]) {
JFrame frame = new JFrame("FmDemo1");
// handle window closing
frame.addWindowListener(
new WindowAdapter() {
public void windowClosing(
WindowEvent e) {
System.exit(0);
}
});
final JPanel panel = new JPanel();
// set up a button and add an
// action listener to it
JButton button = new JButton("Draw Text");
button.addActionListener(
new ActionListener() {
public void actionPerformed(
ActionEvent e) {
Graphics g = panel.getGraphics();
// draw two lines of text
int BASE1 = 100;
int OFFSET1 = 8;
g.drawString("LINE 1", 100, BASE1);
g.drawString("LINE 2",100, BASE1 + OFFSET1);
// draw two lines of text,
// using font metrics
FontMetrics fm = g.getFontMetrics();
int BASE2 = 150;
int OFFSET2 = fm.getHeight();
g.drawString("LINE 1", 100, BASE2);
g.drawString("LINE 2", 100,BASE2 + OFFSET2);
}
});
panel.add(button);
frame.getContentPane().add(panel);
frame.setSize(250, 250);
frame.setLocation(300, 200);
frame.setVisible(true);
}
}
这段程序是可以工作的。选择 Draw Text 按钮,你将看到两行字符跟在另外两行字符之后画出。注意:第一个两行有点挤在一起了。解决这个问题可以修改 8 为一个大一点的数值如 18 。但是这种途径忽略了一个问题。当你正在画的字符是作为一组图像操作的一部分的时候,你的程序需要处理不同字体大小的字符。换句话说,程序需要根据它所处理的字符的大小自动调整。你可以把数值从 8 改编成 18 ,来解决 FmDemo1 例子中的问题,但是如果你处理更大的字符会怎么样呢?这种情况下,高度 18 就不够了。
一种比较好的解决方案在 FmDemo1 的第二组 drawString 语句中进行了说明,他们画了两行小写字符。这个程序获取了一个 FontMetrics 对象。然后,在这个对象中调用 getHeight 方法来获取字符的高度。这个高度被用于替换 8 或 18 这样的固定数值。
通常一个程序通过调用 Graphics.getFontMetrics 来获取一个 FontMetrics 对象。返回的对象实际上是 FontMetrics 的一个子类, FontMetrics 是一个抽象类。 FontMetrics 对象包含着有关一个给定字符的大小的信息。
如果想了解哪些信息有效,让我们类看另一个例子:
package EXP;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FmDemo2 {
public static void main(String args[]) {
JFrame frame = new JFrame("FmDemo2");
// handle window closing
frame.addWindowListener(
new WindowAdapter() {
public void windowClosing(
WindowEvent e) {
System.exit(0);
}
});
// set up a panel and set its font
final JPanel panel = new JPanel();
Font f = new Font("Monospaced", Font.ITALIC, 48);
panel.setFont(f);
// set up a button and action listener for it
JButton button = new JButton("Draw Text");
button.addActionListener(
new ActionListener() {
public void actionPerformed(
ActionEvent e) {
int XBASE = 50;
int YBASE = 100;
String test_string ="hqQWpy`//'|i//,{_!^";
Graphics g = panel.getGraphics();
FontMetrics fm =g.getFontMetrics();
int ascent = fm.getAscent();
int descent = fm.getDescent();
System.out.println("=== ascent "+ascent);
System.out.println("=== descent "+descent);
System.out.println("=== xbase "+XBASE);
System.out.println("=== YBASE "+YBASE);
int width = fm.stringWidth(test_string);
// draw a text string
g.drawString(test_string, XBASE, YBASE);
// draw the ascent line
g.setColor(Color.red);
g.drawLine(XBASE, YBASE - ascent,XBASE + width , YBASE - ascent);
// draw the base line
g.setColor(Color.green);
g.drawLine(XBASE, YBASE,XBASE + width, YBASE);
// draw the descent line
g.setColor(Color.blue);
g.drawLine(XBASE, YBASE + descent,XBASE + width, YBASE + descent);
}
});
panel.add(button);
frame.getContentPane().add(panel);
frame.setSize(600, 250);
frame.setLocation(250, 200);
frame.setVisible(true);
}
}
运行这个程序,并选择 Draw Text 按钮。你将看到右上中下三行线条组成的字符串。绿色的线是在基准线上。这是用于计算字符测量偏差的起始点。在抽象窗口工具集( AWT )画一个字符时,字符的 X,Y 参考点是在基准线的字符的左侧。
顶端的红线上浮线。这是从基准线到所有字符的最上端的偏差。蓝色线条是下浮线。它是从基准线到所有字符最下端的偏差。也可能有些字符有更大的上浮或下浮。 FontMetrics 提供了 getMaxAscent 和 getMaxDescent 方法来获取字符的最大值。还有一个被称为 leading 的属性,用于表示在一行字符的下浮和另一行字符的上浮之间保留的空间。
FmDemo2 同样说明了 stringWidth 方法的使用,用于计算字符串的图像宽度。每个字符串有一个所为的领先宽度。这是 AWT 在画完一个字符后放置另一个字符的位置。一个字符串的领先宽度没有必要计算它的字符的宽度和的绝对数值。因为一些字符的宽度是根据上下文变化的。
让我们在看最后一个例子,它说明了如何在一个字符串周围画边框:
package EXP;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class FmDemo3 {
public static void main(String args[]) {
JFrame frame = new JFrame("FmDemo3");
// handle window closing
frame.addWindowListener(
new WindowAdapter() {
public void windowClosing(
WindowEvent e) {
System.exit(0);
}
});
// set up a panel and set a font for it
final JPanel panel = new JPanel();
Font f = new Font(
"Monospaced", Font.ITALIC, 48);
panel.setFont(f);
JButton button = new JButton("Draw Text");
button.addActionListener(
new ActionListener() {
public void actionPerformed(
ActionEvent e) {
int XBASE = 50;
int YBASE = 100;
String test_string ="hqQWpy`//'|i//,{_!^";
Graphics g = panel.getGraphics();
FontMetrics fm =
g.getFontMetrics();
// draw a text string
g.drawString(
test_string, XBASE, YBASE);
// draw a bounding box around it
RectangularShape rs =fm.getStringBounds(test_string, g);
Rectangle r = rs.getBounds();
g.setColor(Color.red);
g.drawRect(XBASE + r.x,
YBASE + r.y, r.width, r.height);
}
});
panel.add(button);
frame.getContentPane().add(panel);
frame.setSize(600, 250);
frame.setLocation(250, 200);
frame.setVisible(true);
}
}
在 FmDemo3 程序中, getStringBounds 方法被用户获取 RectangularShape 对象。程序接着调用了 getBounds 来获取边框,它是画在文字四周的。这在你希望同时布置文字和图像,并希望知道字符站多大空间时,是非常有用的。