Java学习day069 图形程序设计(四)(文本使用特殊字体、显示图像)

使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。

day069   图形程序设计(四)(文本使用特殊字体、显示图像)


1.文本使用特殊字体

在之前的“Not a Hello,World”程序中用默认字体显示了一个字符串。实际上,经常希望选用不同的字体显示文本。人们可以通过字体名(font face name)指定一种字体。字体名由“Helvetica”这样的字体家族名(font family name)和一个可选的“Bold”后缀组成。例如,“Helvetica”和“Helvetica Bold”属于“Helvetica”家族的字体。

要想知道某台特定计算机上允许使用的字体,就需要调用GraphicsEnvironment类中的getAvailableFontFamilyNames方法。这个方法将返回一个字符型数组,其中包含了所有可用的字体名。GraphicsEnvironment类描述了用户系统的图形环境,为了得到这个类的对象,需要调用静态的getLocalGraphicsEnvironment方法。下面这个程序将打印出系统上的所有字体名:

import java.awt.*;
public class ListFonts
{
    public static void main(String[] args)
    {
        String[] fontNames = GraphicsEnvironment
            .getLocalGraphicsEnvironmentp
            .getAvailableFontFamilyNames();
        for (String fontName : fontNames)
            System.out.println(fontName);
    }
}

在某个系统上,输出的结果为:

Abadi MT Condensed Light
Arial
Arial Black
Arial Narrow
Arioso
Baskerville
Binner Gothic
...

后面还有70种左右的字体。字体名可以商标化,字体设计在一些权限内可以版权化。因此,字体的分发需要向字体的创始者付版税。当然,像名牌香水有廉价仿制品一样,字体也有外观相似的。例如,Helvetica的仿制品就是Windows中称为Arial的字体。为了创建一个公共基准,AWT定义了五个逻辑(logical)字体名:

SansSerif
Serif
Monospaced
Dialog
Dialoglnput

这些字体将被映射到客户机上的实际字体。例如,在Windows系统中,SansSerif将被映射到Arial。

另外,OracleJDK包含3种字体,它们是“LucidaSans”,“LucidaBright”和“LucidaSansTypewriter"。要想使用某种字体绘制字符,必须首先利用指定的字体名、字体风格和字体大小来创建—个Font类对象。下面是构造一个Font对象的例子:

Font sansboldl4 = new Font(°SansSerif", Font.BOLD, 14);

第三个参数是以点数目计算的字体大小。点数目是排版中普遍使用的表示字体大小的单位,每英寸包含72个点。

在Font构造器中,提供字体名的位置也可以给出逻辑字体名称。另外,利用Font构造器的第二个参数可以指定字体的风格(常规、加粗、斜体或加粗斜体),下面是几个字体凤格的值:

Font.PLAIN
Font.BOLD
Font.ITALIC
Font.BOLD+Font.ITALIC

可以读取TrueType或PostScriotType1格式的字体文件。这需要一个字体输人流通常从磁盘文件或者URL读取。然后调用静态方法Font.createFont:

URL url = new URL("http://www.fonts.com/Wingbats.ttf");
InputStreani in =  url.openStream();
Font fl =Font.createFont(Font.TRUETYPE_FONT, in);

上面定义的字体为常规字体,大小为 1。可以使用 deriveFont 方法得到希望大小的字体:

Font f = fl.deriveFont(14.0F);

Java字体包含了通用的ASCII字符和符号。例如,如果用Dialog字体打印字符‘W2297’,那么就会看到®字符。只有在Unicode字符集中定义的符号才能够使用。下面这段代码将使用系统中14号加粗的标准sansserif字体显示字符串“Hello,World”:

Font sansbo1dl4 = new Font("SansSerif", Font.BOLD, 14);
g2.setFont(sansboldl4);
String message = "Hello, World!";
g2.drawString(message, 75, 100);

接下来,将字符串绘制在面板的中央,而不是任意位置。因此,需要知道字符串占据的宽和高的像素数量。这两个值取决于下面三个因素:

•使用的字体(在前面列举的例子中为sansserif,加粗,14号);

•字符串(在前面列举的例子中为“Hello,World”);

•绘制字体的设备(在前面列举的例子中为用户屏幕)。

要想得到屏幕设备字体属性的描述对象,需要调用GraphicS2D类中的getFontRenderContext方法。它将返回一个FontRenderContext类对象。可以直接将这个对象传递给Font类的getStringBounds方法:

FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = sansboldl4..getStringBounds(message, context);

getStringBounds方法将返回包围字符串的矩形。为了解释这个矩形的大小,需要清楚几个排版的相关术语。如图所示。基线(baseline)是一条虚构的线,例如,字母“e”所在的底线。上坡度(ascent)是从基线到坡顶(ascenter)的距离。例如,“b”和“k”以及大写字母的上面部分。下坡度(descent)是从基线到坡底(descenter)的距离,坡底是“p”和“g”这种字母的底线。

                        

行间距(leading)是某一行的坡底与其下一行的坡顶之间的空隙(这个术语源于排字机分隔行的间隔带)。字体的高度是连续两个基线之间的距离,它等于下坡度+行间距+上坡度。

getStringBounds方法返回的矩形宽度是字符串水平方向的宽度。矩形的高度是上坡度、下坡度、行间距的总和。这个矩形始于字符串的基线,矩形顶部的y坐标为负值。因此,可以采用下面的方法获得字符串的宽度、髙度和上坡度:

double stringWidth = bounds.getWidth();
double stringHeight = bounds.getHeight();
double ascent = -bounds.getY();

如果需要知道下坡度或行间距,可以使用Font类的getLineMetrics方法。这个方法将返回一个LineMetrics类对象,获得下坡度和行间距的方法是:

LineMetrics metrics = f.getLineMetrics(message, context);
float descent = raetrics.getDescent();
float leading = metrics.getLeading();

下面这段代码使用了所有这些信息,将字符串显示在包围它的组件中央:

FontRenderContext context = g2.getFontRenderContext();
Rectang1e2D bounds = f.getStringBounds(message, context);

//(x,y) = top left comer 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);

为了能够获得中央的位置,可以使用getWidthO得到组件的宽度。使用bounds.getWidth()得到字符串的宽度。前者减去后者就是两侧应该剩余的空间。因此,每侧剩余的空间应该是这个差值的一半。高度也是一样。

为了说明位置是正确的,下面的程序绘制了基线和包围这个字符串的矩形。

/**
 *@author  zzehao
 */
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;

public class FontTest
{
	public static void main(String[] agrs)
	{
		EventQueue.invokeLater(() ->
		{
			JFrame frame = new FontFrame();
			frame.setTitle("FontTest");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}
}

class FontFrame extends JFrame
{
	public FontFrame()
	{
		add(new FontComponent());
		pack();
	}
}

class FontComponent extends JComponent
{		
	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT = 200;

	public void paintComponent(Graphics g)
	{
		Graphics2D g2 = (Graphics2D) g;

		String message = "Hello,World!";

		Font f = new Font("Serif",Font.BOLD,36);
		g2.setFont(f);

		//measure the size of the message
		FontRenderContext context = g2.getFontRenderContext();
		Rectangle2D bounds = f.getStringBounds(message,context);

		//set(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;

		//draw the message
		g2.drawString(message,(int)x,(int)baseY);
		g2.setPaint(Color.LIGHT_GRAY);

		//draw the baseline
		g2.draw(new Line2D.Double(x,baseY,x+bounds.getWidth(),baseY));

		//draw the enclosing rectangle
		Rectangle2D rect = new Rectangle2D.Double(x,y,bounds.getWidth(),bounds.getHeight());
		g2.draw(rect);
	}

	public Dimension getPreferredSize()
	{
		return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
	}
}

运行的结果:

                           

                   

                     


2.显示图像

到目前为止,我们已经看到了如何通过绘制直线和图形创建一个简单的图像。而对于照片这样的复杂图像来说,通常都是由扫描仪或特殊的图像处理软件生成的。

一旦图像保存在本地文件或因特网的某个位置上,就可以将它们读到Java应用程序中,并在Graphics对象上进行显示。读取图像有很多方法。在这里我们使用你之前已经见过的Imagelcon类:

Image image=new Imagelcon ( filename) .getImage();

这里的变量image包含了一个封装图像数据的对象引用。可以使用Graphics类的drawlmage方法将图像显示出来。

public void paintComponent(Graphics g)
{
    ....
    g.drawlmage(image, x, y, null);
}

下面的又前进了一步,它在一个窗口中平铺显示了一幅图像。屏幕显示的结果如图所示。这里采用paintComponent方法来实现平铺显示。它的基本过程为:先在左上角显示图像的一个拷贝,然后使用copyArea将其拷贝到整个窗口:

                                             

for(int i=0;i*imageWidth<=getWidth();i++)
		{
			for(int j = 0;j*imageHeight<=getHeight();j++)
			{
				if(i+j>0)
				{
				   g.copyArea(0,0,imageWidth,imageHeight,i*imageWidth,j*imageHeight);
				}
			}
		}

完整的代码:

/**
 *@author  zzehao
 */
import java.awt.*;
import javax.swing.*;

public class ImageTest
{
	public static void main(String[] agrs)
	{
		EventQueue.invokeLater(() ->
		{
			JFrame frame = new ImageFrame();
			frame.setTitle("ImageTest");
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}
}

class ImageFrame extends JFrame
{
	public ImageFrame()
	{
		add(new ImageComponent());
		pack();
	}
}

class ImageComponent extends JComponent
{		
	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT = 200;

	private Image image;

	public ImageComponent()
	{
		image = new ImageIcon("2.gif").getImage();
	}

	public void paintComponent(Graphics g)
	{
		if(image == null)
			return;

		int imageWidth = image.getWidth(this);
		int imageHeight = image.getHeight(this);

		//draw the image in the upper-left corner
		g.drawImage(image,0,0,null);

		//tile the image across the component
		for(int i=0;i*imageWidth<=getWidth();i++)
		{
			for(int j = 0;j*imageHeight<=getHeight();j++)
			{
				if(i+j>0)
				{
					g.copyArea(0,0,imageWidth,imageHeight,i*imageWidth,j*imageHeight);
				}
			}
		}
	}

	public Dimension getPreferredSize()
	{
		return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
	}
}

                      


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值