使用Graphics2D类的setPaint方法可以为图形环境上的所有后续的绘制操作选择颜色。要想绘制多种颜色,就需要按照选择颜色,绘制图形,再选择颜色,再绘制图形的过程实施。
Color类用于定义颜色。在java.awt.Color类中提供了13个预定义的常量,它们分别表示13种标准颜色,如表7-1所示。
例如:
g2.setPaint(Color.RED);
g2.drawString("Warning!", 100, 100);
注意:在JDK 1.4之前的版本中,标准颜色的名字为小写形式,例如,Color.red。这似乎有些超出寻常,因为标准编码的惯例是采用大写形式书写常量。从JDK 1.4开始,可以采用大写的形式书写标准颜色的名字,不过,为了向后兼容,也可以用小写形式书写。
可以通过提供红、绿和蓝三色成分来创建一个Color对象,以达到定制颜色的目的。三种颜色都是用0~255(也就是一个字节)之间的整型数值表示,调用Color的构造器格式为:
Color(int redness, int greenness, int blueness)
下面是一个定制颜色的例子:
g2.setPaint(new Color(0, 128, 128)); //a dull blue-green
g2.drawString("Welcome!", 75, 125);
注意:除了纯色以外,还可以选择更复杂的“颜料”设置,例如,改变色调(hue)或者图像。有关这方面更加详细的内容请参阅卷II中的高级AWT章节。如果使用Graphics对象,而不是Graphics2D对象,就需要使用setColor方法设置颜色。
要想设置背景颜色,就需要使用Component类中的setBackground方法。Component类是JPanel类的祖先。
MyPanel p = new MyPanel( );
p.setBackground(Color.PINK);
另外,还有一个setForeground方法,它是用来设定在组件上进行绘制时使用的默认颜色。
提示:从名字就可以看出,Color类中的brighter( )方法和darker( )方法的功能,它们分别加亮或变暗当前的颜色。使用brighter方法也是加亮条目的好办法。实际上,brighter( )只微微地加亮一点。要达到耀眼的效果,应该调用这个方法三次:c.brighter( ).brighter( ).brighter( )。
Java在SystemColor类中预定义了很多颜色的名字。在这个类中的常量,封装了用户系统的各个元素的颜色。例如,
p.setBackground(SystemColor.window)
它将把面板的背景颜色设定为用户桌面上所有窗口使用的默认颜色。(无论何时重新绘制窗口,都会填充背景颜色。)当希望让绘制的用户界面元素与用户桌面上已经存在的其他元素的颜色匹配时,使用SystemColor类中的颜色非常有用。表7-2列出了系统颜色的名字和它们的含义。
java.awt.Color 1.0
• Color(int r, int g, int b)
创建一个颜色对象。
参数:r 红色值(0~255)
g 绿色值(0~255)
b 蓝色值(0~255)
java.awt.Graphics 1.0
• void setColor(Color c)
改变当前的颜色。所有后续的绘图操作都使用这个新颜色。
参数:c 新颜色
java.awt.Graphics2D 1.2
• void setPaint(Paint p)
设置这个图形环境的绘制属性。Color类实现了Paint接口。因此,可以使用这个方法将绘制属性设置为纯色。
java.awt.Component 1.0
• void setBackground(Color c)
设置背景颜色。
参数:c 新背景颜色
• void setForeground(Color c)
设置前景颜色。
参数:c 新前景颜色
填充图形
可以选用一种颜色(通常,用当前的绘制颜色)填充闭合图形(例如,矩形或椭圆)的内部。要想填充图形,只需要将draw替换为fill就可以了:
Rectangle2D rect = . . .;
g2.setpaint(Color.RED);
g2.fill(rect); //fills rect with red color
在例7-5的程序中先用红色填充一个矩形,然后
再用暗绿色填充该矩形的内接椭圆,如图7-12所示。
例7-5 FillTest.java
在本章开始的“Not a Hello, World”程序中用默认字体显示了一个字符串。实际上,经常希望选用不同的字体显示文本。人们可以通过字体名(font face name)指定一种字体。字体名由“Helvetica”这样的字体家族名(font family name)和一个可选的“Bold”后缀组成。例如,
“Helvetica”和“Helvetica Bold”属于“Helvetica”家族的字体。
要想知道某台特定计算机上所允许使用的字体,就需要调用GraphicsEnvironment类中的 getAvailableFontFamilyNames方法。这个方法将返回一个字符型数组,其中包含了所有可用的字体名。
GraphicsEnvironment类描述了用户系统的图形环境,为了得到这个类的对象,需要调用静态的 getLocalGraphicsEnvironment方法。下面这个程序将打印出系统上的所有字体名:
在某个系统上,输出的结果为:
后面还有70种左右的字体。
注意:JDK文档认为,后缀“heavy”、“medium”、“oblique”或“gothic”是一个家族内部的变体。根据我们的经验,事实并非如此。“Bold”、“Italic”和“BoldItalic”后缀是一个家族变体,而其他的后缀则不然。
遗憾的是,无法知道用户是否安装了某种特定“外观”的字体。字体名可以商标化,字体设计在一些权限内可以版权化。因此,字体的分发需要向字体的创始者支付特许使用金。当然,与名牌香水有廉价仿制品一样,字体也有外观相似的仿制品。例如,Helvetica 的仿制品就是Windows中被称为Arial的字体。
为了创建一个公共基准,AWT定义了五个逻辑(logical)字体名:
SansSerif
Serif
Monospaced
Dialog
DialogInput
这些字体将被映射到客户机上的实际字体。例如,在Windows系统中,SansSerif将被映射到Arial上。
注意:字体映射定义在Java安装的jre/lib子目录中的fontconfig.properties文件中。有关这个文件的更详细信息请参阅 http://java.sun.com/j2se/5.0/docs/guide/intl/fontconfig.html。
早期版本的JDK使用的font.properties文件现在已经作废。
要想使用某种字体绘制字符,必须首先利用指定的字体名、字体风格和字体大小来创建一个Font类对象。下面是构造一个Font对象的例子:
Font helvb14 = new Font("Helvetica", Font.BOLD, 14);
第三个参数是以点的数目计算的字体大小。点的数目是排版中普遍使用的表示字体大小的单位,每英寸 包含72个点。这条语句使用的是14个点的字体。
在Font构造器中,提供字体名的位置也可以给出逻辑字体名称。另外,利用Font构造器的第二个参数可以指定字体的风格(常规、加粗、斜体或加粗斜体),下面是几个字体风格的值:
Font.PLAINFont.BOLDFont.ITALICFont.BOLD+Font.ITALIC
下面是一个例子:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14)
注意:在Java以前的版本中,将Helvetica、TimesRoman、Courier和ZapfDingbats作为逻辑字体名。为了向后兼容,现在仍然将这些字体名按照逻辑字体名对待,尽管Helvetica实际上是一种字体名,而TimesRoman和ZapfDingbats根本不是字体名,它们实际的字体名是“Times Roman”和“Zapf Dingbats”。
提示:从JDK 1.3开始,可以读取TrueType字体。这需要一个字体输入流—通常从磁盘文件或者URL读取。(有关流的更详细信息请参阅第12章。)然后调用静态方法Font.createFont:
URL url = new URL("http://www.fonts.com/Wingbats.ttf");InputStream in = url.openStream( );Font f = Font.createFont(Font.TRUETYPE_FONT, in);
上面定义的字体为常规字体,大小为1。可以使用deriveFont方法定义字体的大小:
Font df = f.deriveFont(14.0F);
警告:deriveFont方法有两个重载版本。一个(有一个float参数)设置字体的大小;另一个(有一个int参数)设置字体风格。所以f.deriveFont(14)设置的是字体风格,而不是大小!(其结果为斜体,因为14的二进制表示的是ITALIC,而不是BOLD。)
Java字体包含了通用的ASCII字符和符号。例如,如果用Dialog字体打印字符'\u2297',那么就会看到 字符。只有在Unicode字符集中定义的符号才能够使用。
下面这段代码将使用系统上14号加粗的标准sans serif字体显示字符串“Hello, World”:
Font sansbold14 = new Font("SansSerif", Font.BOLD, );
g2.setFont(sansbold14);String message = "Hello, World!";
g2.drawString(message, , );
接下来,将字符串绘制在面板的中央,而不是任意位置。因此,需要知道字符串占据的宽和高的像素数量。这两个值取决于下面三个因素:
• 使用的字体(在前面列举的例子中为sans serif,加粗,14号)。
• 字符串(在前面列举的例子中为“Hello, World”)。
• 绘制字体的设备(在前面列举的例子中为用户屏幕)。
要想得到屏幕设备字体属性的描述对象,需要调用Graphics2D类中的getFontRenderContext方法。
它将返回一个FontRenderContext类对象。可以直接将这个对象传递给Font类的getStringBounds方法:
FontRenderContext context = g2.getFontRenderContext( );
Rectangle2D bounds = f.getStringBounds(message, context);
getStringBounds方法将返回包围字符串的矩形。
为了解释这个矩形的大小,需要清楚几个排版的相关术语。如图7-13所示。基线(baseline)是一条虚构的线,例如,字母“e”所在的底线。上坡度(ascent)是从基线到坡顶(ascenter)的距离。例如,“b”和“k”以及大写字母的上面部分。下坡度(descent)是从基线到坡底(descenter)的距离,坡底是“p”和“g”这种字母的底线。
行间距(leading)是某一行的坡底与其下一行的坡顶之间的空隙(这个术语源于排字机分隔行的引导带)。字体的高度是连续两个基线之间的距离,它等于下坡度+行间距+上坡度。
getStringBounds方法返回的矩形宽度是字符串水平方向的宽度。矩形的高度是上坡度、下坡度、行间距的总和。该矩形始于字符串的基线,矩形顶部的y坐标为负值。因此,可以采用下面的方法获得字符串的宽度、高度和上坡度:
如果需要知道下坡度或行间距,可以使用Font类的getLineMetrics方法。这个方法将返回一个LineMetrics类对象,获得下坡度和行间距的方法是:
下面这段代码使用了所有这些信息,将字符串显示在包围它的面板中央:
为了能够获得中央的位置,可以使用getWidth( )得到面板的宽度。使用bounds.getWidth( )得到字符串的宽度。前者减去后者就是两侧应该剩余的空间。因此,每侧剩余的空间应该是这个差值的一半。高度也是一样。
最后,程序绘制出基线和包围该字符串的矩形。
图7-14给出了屏幕显示结果。例7-6是程序清单。
例7-6 FontTest.java
java.awt.Font 1.0
• Font(String name, int style, int size)
创建一个新字体对象。
参数:name
字体名。不是字体名(“Helvetica Bold”),就是逻辑字体名(“Serif”、“SansSerif”)style
字体风格(Font.PLAIN、Font.BOLD、Font.ITALIC或Font.BOLD+Font.ITALIC)size字体大小(例如,12)
• String getFontName( )
返回字体名,例如,“Helvetica Bold”。
• String getFamily( )
返回字体家族名,例如,“Helvetica”。
• String getName( )
如果采用逻辑字体名创建字体,将返回逻辑名,例如,“SansSerif”;否则,返回字体名。
• Rectangle2D getStringBounds(String s, FontRenderContext context) 1.2返回包围该字符串的矩形。矩形的起点为基线。矩形顶端的y坐标等于上坡度的负值。矩形的高度等于上坡度、下坡度和行间距之和。宽度等于字符串的宽度。
• LineMetrics getLineMetrics(String s, FontRenderContext context) 1.2
返回确定字符串宽度的一个线性metrics对象。
• Font deriveFont(int style) 1.2
• Font deriveFont(float size) 1.2
• Font deriveFont(int style, float size) 1.2
返回一个新字体,除给定大小和字体风格外,其他与原字体一样。
java.awt.font.LineMetrics 1.2
• float getAscent( )
返回字体的上坡度—从基线到大写字母顶端的距离。
• float getDescent( )
返回字体的下坡度—从基线到坡底的距离。
• float getLeading( )
返回字体的行间距—从一行文本底端到下一行文本顶端之间的空隙。
• float getHeight( )
返回字体的总高度—两条文本基线之间的距离(下坡度+行间距+上坡度)。
java.awt.Graphics 1.0
• void setFont(Font font)
为图形环境选择一种字体。这种字体将被应用于后续的文本绘制操作中。
参数:font
字体
• void drawString(String str, int x, int y)
采用当前字体和颜色绘制一个字符串。
参数:str 将要绘制的字符串
x 字符串开始的x坐标
y 字符串基线的y坐标
java.awt.Graphics2D 1.2
• FontRenderContext getFontRenderContext( )
返回这个图形环境中,指定字体特征的字体绘制环境。
• void drawString(String str, float x, float y)
采用当前的字体和颜色绘制一个字符串。
参数:str 将要绘制的字符串
x 字符串开始的x坐标
y 字符串基线的y坐标
到目前为止,已经看到了如何通过绘制直线和图形创建一个简单的图像。而对于照片这样的复杂图像来说,通常都是由扫描仪或特殊的图像处理软件生成的。(正像在卷II中将看到的,逐像素地生成图像,并将结果存储到数组中也是可以的。这种方式通常用于生成不规则碎片的图像。)
一旦图像保存在本地文件或因特网的某个位置上,就可以将它们读到Java应用程序中,并在Graphics对象上进行显示。在JDK 1.4中,读取一个图像十分简单。如果图像存储在本地文件中,就应该调用:
String filename = ". . .";
Image image = ImageIO.read(new File(filename));
否则,应该提供URL:
String urlname = ". . .";
Image image = ImageIO.read(new URL(urlname));
如果图像不可用,read方法将抛出一个IOException。在第11章中,将讨论有关异常处理的问题。
而在目前的例子程序中只捕获异常,并打印出栈的轨迹。
这里的变量image包含了一个封装图像数据的对象引用。可以使用Graphics类的drawImage方法将图像显示出来。
例7-7又前进了一步,它在一个窗口中平铺显示了一幅图像。屏幕显示的结果如图7-15所示。这里采用paintComponent方法来实现平铺显示。它的基本过程为:先在左上角显示图像的一个拷贝,然后使用copyArea将其拷贝到整个窗口:
注意:如果在JDK 1.3或早期版本中加载一幅图像,就应该使用MediaTracker类。媒体跟踪器可以跟踪获得的一幅或多幅图像。(名字“媒体”暗示着这个类可以跟踪音频文件或其他媒体的文件。这预示着未来的发展,当前仅实现了跟踪图像。)
可以使用下列命令将一幅图像添加到跟踪器对象中:
MediaTracker tracker = new MediaTracker(component);
Image img = Toolkit.getDefaultToolkit( ).getImage(name);
int id = 1; //the ID used to track the image loading process
tracker.addImage(img, id);
可以将多个图像添加到一个媒体跟踪器中。每一幅图像都应该有一个不同的ID值,但是可以选择任何一种方便的计数方式。为了等待图像全部加载完毕,可以使用下面这样的代码:
try { tracker.waitForID(id); }
catch (InterruptedException e) {}
如果想获得多幅图像,可以将它们都添加到媒体跟踪器对象,并等到全部加载进来为止。可以使用下列代码实现这项操作:
try { tracker.waitForAll( ); }
catch (InterruptedException e) {}
例7-7列出了图像显示程序的全部源代码。到此为止,我们将结束Java图像编程的讨论。有关更加高级的技术,请参阅卷II中有关2D图形和图像处理的论述。
例7-7 ImageTest.java
java.swing.ImageIO 1.4
• static BufferedImage read(File f)
• static BufferedImage read(URL u)
从给定文件或URL上读取图像。
java.awt.Image 1.0
• Graphics getGraphics( )
返回一个图形环境,以便绘制该图像缓冲区。
• void flush( )
释放该图像缓冲区中保存的所有资源。
java.awt.Graphics 1.0
• boolean drawImage(Image img, int x, int y, ImageObserver observer)
绘制一幅非比例图像。注意:这个调用可能会在图像还没有绘制完毕就返回。
参数:img 将要绘制的图像
x 左上角的x坐标
y 左上角的y坐标
observer
绘制进程中以通告为目的的对象(可能为null)
• boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
绘制一幅比例图像。系统按照比例将图像放入给定宽和高的区域。注意:这个调用可能会在图像还没有绘制完毕就返回。
参数:img 将要绘制的图像
x 左上角的x坐标
y 左上角的y坐标
width 描述图像的宽度
height 描述图像的高度
observer 绘制进程中以通告为目的的对象(可能为null)
• void copyArea(int x, int y, int width, int height, int dx, int dy)
拷贝屏幕的一块区域。
参数:x 原始区域左上角的x坐标
y 原始区域左上角的y坐标
width 原始区域的宽度
height 原始区域的高度
dx 原始区域到目标区域的水平距离
dy 原始区域到目标区域的垂直距离
• void dispose( )
释放图形环境和操作系统资源。必须释放由调用Image.getGraphics这样的方法获得的图形环境,但不要释放由paintComponent获得的图形环境。
java.awt.Component 1.0
• Image createImage(int width, int height)
创建一个用于双缓冲的脱屏图像缓冲区。
参数:width 图像的宽度
height 图像的高度
java.awt.MediaTracker 1.0
• MediaTracker(Component c)
跟踪在给定组件中显示的图像。
• void addImage(Image image, int id)
将一个图像添加到被跟踪的图像列表中。当图像添加完毕后,图像加载进程将启动。
参数:image 被跟踪的图像
id 稍后引用该图像的标识符
• void waitForID(int id)
等待指定ID的图像加载完毕。
• void waitForAll( )
等待所有被跟踪的图像都加载完毕。
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。