百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

带你学java核心技术图形程序设计:颜色+为文本设定特殊字体+图像

myzbx 2025-02-13 13:15 33 浏览

颜色

使用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.PLAIN
Font.BOLD
Font.ITALIC
Font.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, 14);
g2.setFont(sansbold14);
String message = "Hello, World!";
g2.drawString(message, 75, 100);

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

? 使用的字体(在前面列举的例子中为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( )

等待所有被跟踪的图像都加载完毕。

觉得文章不错的话,可以转发关注小编一下!!!

明天更新事件处理基础、动作、AWT事件继承层次、AWT的语义事件和低级事件、低级事件类型。

相关推荐

零基础入门AI智能体:详细了解什么是变量类型、JSON结构、Markdown格式

当品牌跳出固有框架,以跨界联动、场景创新叩击年轻群体的兴趣点,一场关于如何在迭代中保持鲜活的探索正在展开,既藏着破圈的巧思,也映照着与新一代对话的密码。在创建AI智能体时,我们会调用插件或大模型,而在...

C# 13模式匹配:递归模式与属性模式在真实代码中的性能影响分析

C#13对模式匹配的增强让复杂数据处理代码更简洁,但递归模式与属性模式的性能差异一直是开发者关注的焦点。在实际项目中,选择合适的模式不仅影响代码可读性,还可能导致执行效率的显著差异。本文结合真实测试...

零基础快速入门 VBA 系列 6 —— 常用对象(工作簿、工作表和区域)

上一节,我介绍了VBA内置函数以及如何自动打字和自动保存文件。这一节,我们来了解一下Excel常用对象。Excel常用对象Excel有很多对象,其中最常用也最重要的包括以下3个:1.Workbo...

不同生命数字的生肖龙!准到雷普!

属龙的人总在自信爆棚和自讨苦吃之间反复横跳?看完这届龙宝宝的日常我悟了。属龙的人好像天生自带矛盾体:领导力超强可人缘时好时坏,工作雷厉风行却总在爱情里翻车。关键年份的龙性格差异更大——76年龙靠谱但不...

仓颉编程语言基础-面向对象编程-属性(Properties)

属性是仓颉颉中一种强大的机制,它允许你封装对类(或接口interface、结构体struct、枚举enum、扩展extend)内部状态的访问。它看起来像一个普通的成员变量(字段),但在其背后,它通过...

Python中class对象/属性/方法/继承/多态/魔法方法详解

一、基础入门:认识类和对象1.类和对象的概念在Python中,类(class)是一种抽象的概念,用于定义对象的属性和行为,而对象(也称为实例)则是类的具体表现。比如,“汽车”可以是一个类,它有...

VBA基础入门:搞清楚对象、属性和方法就成功了一半

如果你刚接触VBA(VisualBasicforApplications),可能会被“对象”“属性”“方法”这些术语搞得一头雾水。但事实上,这三个概念是VBA编程的基石。只要理解它们之间的关系,...

P.O类型文推荐|年度编推合集(一百九十五篇)

点击左上方关注获取更多精彩推文目录2019年度编推35篇(1V1)《悖论》作者:流苏.txt(1V1)《桂花蒸》作者:大姑娘浪.txt(1V1)《豪门浪女》作者:奚行.txt...

Python参数传递内存大揭秘:可变对象 vs 不可变对象

90%的Python程序员不知道,函数参数传递中可变对象的修改竟会导致意想不到的副作用!一、参数传递的本质:对象引用传递在Python中,所有参数传递都是对象引用的传递。这意味着函数调用时传递的不是对...

JS 开发者必看!TC39 2025 最新动向,这些新语法要火?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。TC39第...

2025 年值得尝试的 5 个被低估的 JavaScript 库

这些JavaScript库可能不会在社交媒体或HackerNews上流行起来,但它们会显著提高您的工作效率和代码质量。JavaScript不再只是框架。虽然React、Vue和Sv...

Python自动化办公应用学习笔记30—函数的参数

一、函数的参数1.形参:o定义:在函数定义时,声明在函数名后面括号中的变量。o作用:它们是函数内部的占位符变量,用于接收函数被调用时传入的实际值。o生命周期:在函数被调用时创建,在函数执...

16种MBTI人格全解析|测完我沉默了三秒:原来我是这样的人?

MBTI性格测试火了这么久,你还不知道自己是哪一型?有人拿它当社交话题,有人拿它分析老板性格,还有人干脆当成择偶参考表。不废话,今天我一次性给你整理全部16种MBTI人格类型!看完你不仅能知道自己是谁...

JS基础与高级应用: 性能优化

在现代Web开发中,性能优化已成为前端工程师必须掌握的核心技能之一。本文从URL输入到页面加载完成的全过程出发,深入分析了HTTP协议的演进、域名解析、代码层面性能优化以及编译与渲染的最佳实践。通过节...

爱思创CSP-J/S初赛模拟赛线上开赛!助力冲入2024年CSP-J/S复赛!

CSP-J/S组初赛模拟赛爱思创,专注信奥教育19年,2022年CSP-J/S组赛事指定考点,特邀NOIP教练,开启全真实CSP-J/S组线上初赛模拟大赛!一、比赛对象:2024年备考CSP-J/S初...