作者:顾振宇
您可以自由下载本文,但请保留作者信息。
Java语言的内置类库对多媒体技术的支持能力相当强,尤其是对文本、图形、图像、
声音等媒体的处理与展示均提供了极其方便而又丰富的接口。更令人兴奋不已的是,综合
运用这些媒体所编制出来的一个个Java小应用程序(Applet),使向来冷冰冰的静态的Web
主页(Homepage)上居然展现出一番热热闹闹的动态的新景观,这便是著名的Java动画。在
这一章里,我们就将进入到Java多姿多彩的多媒体世界中去。
如何利用Java的绘图方法来绘制各式各样图形以及显示各种文本字体,并配以所喜爱 颜色,可以说是Java多媒体技术中的一项基本功,也是这一节将要介绍的主要内容。
Java语言的类库中提供了丰富的绘图方法(method),其中大部分对图形、文本、图 像(image)的操作方法都定义在Graphics类中。我们已经知道,Graphics类又是java.awt 程序包的一部分,因此,每当我们要进行图形、文本、图像的处理时,不要忘了在Java源 文件的头部先写上: import java.awt.Graphics; 在这里要特别指出的是,当我们想要在屏幕上绘制图形、文本、图像时,并不需要直 接使用new来产生一个Graphics类的对象实例,而在java.awt.Applet类的paint( )方法(见 上一章)中,我们已经得到了一个Graphics对象的引用,这是系统直接将生成好的Graphics 对象通过参数形式传递给paint( )方法。因此,我们只要在这个对象上进行图形、文本及 图像的绘制操作,就可以在屏幕上看到所显示的结果。
为了将某一图形在屏幕上绘制出来,我们首先要碰到的问题也许就是“画在哪个位置 ”,为了解决这个问题就必须有一个精确的图形坐标系统来将该图形定位。 与大多数其它计算机图形系统所采用的二维坐标系统一样,Java的坐标原点(0,0)在 屏幕的左上角,水平向右为X轴的正方向,竖直向下为Y轴的正方向,每个坐标点的值表示 屏幕上的一个象素点的位置,因此,所有坐标点的值都取整数。图4-1表示用此图形坐标 系统在屏幕上绘制一个矩形。
在Java的Graphics类中提供画线功能的是drawLine( )方法,其调用格式如下: drawLine(int x1,int y1,int x2,int y2) 该方法需要设置四个参数,其中x1,y1表示线段的一个坐标点,x2,y2表示线段的另一 个坐标点。如下面这段程序画出两条线段,其显示结果如图4-2所示。
图4-1 图形坐标系统 1: import java.awt.Graphics; 2: public class Lines extends java.applet.Applet{ 3: public void paint(Graphics g){ 4: g.drawLine(30,30,70,70); 5: g.drawLine(60,50,60,50); 6: } 7: }
图4-2 一条线段与一个点 由于Graphics类不专门提供画点的方法,所以程序中第5行将线段的两个点的坐标均设 为(60,50),因而就相当于在此处画了一个点。
Graphics类中提供了三种类型的矩形,它们分别是普通矩形、圆角矩形和立体矩形。 而每一种矩形都提供两种不同风格的方法,一种是仅画出矩形的边框;另一种是不仅画出 边框,并且还用相同的颜色将整个矩形区域填满。 (1)普通矩形 画普通矩形需调用drawRect( )或fillRect( )方法,它们的调用格式如下: drawRect(int x, int y, int width, int height) //边框型风格 fillRect(int x, int y, int width, int height) //填充型风格 其中头两个参数分别表示矩形左上角的x坐标和y坐标,后两个参数分别表示矩形的宽 度和高度。如下面的paint( )方法画出两个矩形,其显示结果如图4-2所示。 public void paint(Graphics g){ g.drawRect(40,20,60,40); g.fillRect(120,20,60,40); }
图4-3 普通矩形的例子 (2)圆角矩形 圆角矩形,也就是矩形的四个顶角呈圆弧状,每个圆弧其实是由四分之一的椭圆弧所 构成。画圆角矩形的两个方法的调用格式如下: drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) 我们可以看出,它们除了具有和普通矩形含义相同的前四个参数外,还多了两个用来 描述圆角性质的参数。其中arcWidth代表了圆角弧的横向直径;arcHeight代表了圆角弧的 纵向直径。例如图4-4中左边一个圆角矩形所设的圆角参数为(arcWidth=80, arcHeight= 30),其效果就相当于该圆角弧存在于一个长40宽15的小矩形中;而右边一个圆角矩形的 圆角参数为(arcWidth=100, arcHeight=60),并且和整个圆角矩形的width和height参数值 相等,因而该圆角矩形实际变成了一个椭圆。
图4-4 圆角矩形的参数设置
下面的paint( )方法画出三个圆角矩形,显示效果如图4-5所示。我们不难发现,随 着arcWidth和arcHeight值的增大,矩形的圆角就越圆。 public void paint(Graphics g){ g.drawRoundRect(20,20,80,60,20,20); g.fillRoundRect(120,20,80,60,40,30); g.drawRoundRect(220,20,80,60,60,40);}
图4-5 圆角矩形的例子 (3)立体矩形 立体矩形也可以说是三维矩形。其实,Java中的立体矩形并非真正的三维图形,而仅 仅是在矩形的边框上增加一点阴影,使矩形看上去相对表平面好象有凸出或凹下的效果, 其调用方法的格式如下: draw3DRect(int x, int y, int width, int height, boolean raised) fill3DRect(int x, int y, int width, int height, boolean raised) 这两个方法中的前四个参数与drawRect( )方法中所用的参数含义是一样的,第五个参 数raised便是定义该立体矩形是具有凸出(值为true)还是凹下(值为false)的效果。例 如,下面的paint( )方法中,分别画了一个凸出的和一个凹下的矩形。其显示效果如图 4-6所示。确实,由于Java立体矩形中的阴影实在太薄,立体效果当然也就不太明显, 图4-6右边是一个放大了的凹角形状。 public void paint(Graphics g){ g.draw3DRect(20,20,80,60,true); g.fill3DRect(120,20,80,60,false); }
图4-6 立体矩形的例子
多边形的画法通常是给出一组坐标点,再用直线段将这些点依次连接起来。Graphics 类中也提供两个方法来画多边形,一个是边框型drawPolygon( )方法,另一个是填充型 fillPolygon( )方法,并且每一种方法都有两种不同的参数类型。第一种参数类型的调用 格式为: drawPolygon(int xPoints[],int yPoints[],int nPoints) fillPolygon(int xPoints[],int yPoints[],int nPoints) 其中xPoints参数是一个整数数组,用以存放多边形坐标点的X坐标值,yPoints参数 存放相应的一组Y坐标值,nPoints则表示共有几个坐标点。如下面的paint( )方法分别画 了一个边框型和一个填充型的多边形,其显示效果如图4-7所示。 public void paint(Graphics g){ int Poly1_x[]={30,63,115,72,67}; int Poly1_y[]={40,20,95,74,106}; int Poly1_pts=Poly1_x.length; int Poly2_x[]={180,213,265,222,217}; int Poly2_y[]={40,20,95,74,106}; int Poly2_pts=Poly2_x.length; g.drawPolygon(Poly1_x,Poly1_y, Poly1_pts); g.fillPolygon(Poly2_x,Poly2_y, Poly2_pts); }
图4-7 多边形的例子 可以看出,边框型多边形并不自动关闭多边形的最后一条边,而仅是一段开放的折线 。所以,若想画封闭的边框型多边形,不要忘了在数组的尾部再添上一个起始点的坐标。 上述两个画多边形方法的第二种参数传递形式为: drawPolygon(Polygon p) fillPolygon(Polygon p) 其中Polygon是定义在java.awt中的一个类,它的构造方法也有两种不同的参数传递 形式,一种与drawPolygon( )方法的第一种调用格式一样: Polygon(int xPoints[],int yPoints[],int nPoints) 另一种调用格式则是创建一个空的多边形(无参数): Polygon( ) 那为什么要另外创建Polygon对象?生成一个空的Polygon对象又有何用呢?原来, Polygon类中提供了一系列特有的方法,可以方便的进行与多边形相关的操作,象其中的 addPoint( )方法可将多边形的坐标点动态地增加到Polygon对象中。如下面列出的paint( ) 方法所执行的结果与图4-7所示的结果是一样的。 public void paint(Graphics g){ int Poly1_x[]={30,63,115,72,67}; int Poly1_y[]={40,20,95,74,106}; int Poly1_pts=Poly1_x.length; Polygon poly1= new Polygon(Poly1_x,Poly1_y, Poly1_pts); Polygon poly2= new Polygon(); poly2.addPoint(180,40); poly2.addPoint(213,20); poly2.addPoint(265,95); poly2.addPoint(222,74); poly2.addPoint(217,106); g.drawPolygon(poly1); g.fillPolygon(poly2); }
在Java中绘制椭圆的方法是给出该椭圆的外接矩形作为参数,其调用格式与画普通矩 形的方法相似: drawOval(int x, int y, int width, int height) //边框型风格 fillOval(int x, int y, int width, int height) //填充型风格 这里要特别注意:x和y不是椭圆的圆心坐标,而是该椭圆外接矩形的左上角。因此, 画椭圆时,把它先看作是一个矩形将有助于在坐标系统中定位。另外,Graphics类不专门 提供画圆的方法,而只需将width与height参数置成相等就可以了。例如,下面的paint( ) 方法,画出一个圆和一个用颜色填充的椭圆,其显示效果如图4-8所示。 public void paint(Graphics g){ g.drawOval(30,20,60,60); g.fillOval(130,20,80,60); }
图4-8 椭圆的例子
弧是椭圆的一部分,因而画弧的方法就相当于先画一个椭圆,而后取该椭圆中所需要 的一部分。它们的调用格式如下: drawArc(int x, int y, int width, int height,int startAngle, int arcAngle) //边框型风格 fillArc(int x, int y, int width, int height,int startAngle, int arcAngle) //填充型风格 其中前四个参数的含义与画椭圆一样,因此也必须用矩形的观点来确定弧在坐标系统 中的位置。后两个参数就是用来定义椭圆的一部分:startAngle参数表示该弧从什么角度 开始,arcAngle参数表示从startAngle开始转了多少度。如图4-9的弧度坐标系所示,水 平向右表示0度,逆时钟方向为正角度值,顺时钟方向为负角度值。
图4-9 弧度坐标系 如果startAngle和arcAngle中有任一值大于360度的话,都会被转换为0到360度之间的 数值。因此若要画满整个椭圆,arcAngle需设为360的整数倍,若设为370度则相当于只画 了10度。另外fillArc( )方法的效果并不是填充弧的两个端点直接连线所围的区域,而是 填充弧的两端点与圆心连线所围的扇形区域,象一个饼图。下面的paint( )方法画了 图4-9中的几段弧,其显示效果如图4-10所示。 public void paint(Graphics g){ g.drawArc(10,20,100,60,35,65); g.drawArc(110,20,100,60,35,-140); g.fillArc(210,20,100,60,35,65); g.fillArc(310,20,100,60,35,-140); }
图4-10 弧的例子
当我们需要在屏幕上重复绘制一些相同的图形时,也可采用Graphics类中的copyArea( ) 方法,它能将屏幕上某一矩形区域里的内容复制到屏幕的另一区域。其调用格式如下: copyArea(int x, int y, int width, int height, int dx, int dy) 前四个参数我们应该是相当熟悉了,它定义了要被复制的屏幕的矩形区域。最后两个 参数则表示新区域与原始屏幕区域的偏移距离:若dx,dy为正值,则表示新区域相对于原 区域的右方及下方所偏移的像素值;反之,它们取负值则分别表示相对左方及上方的偏 移量。 若要清除屏幕的某一矩形区域所画的内容,就要选用clearRect( )方法,它用当前的 背景颜色来填充整个矩形区域。其调用格式为: clearRect(int x, int y, int width, int height) 可以看出这四个参数定义了所要清除的矩形区域。例如,想要清除整个applet区域, 可先调用Applet类的size( )方法得到整个applet的宽度和高度(该方法没有参数,返回值 是一个Dimension对象,该对象具有width和height实例变量),再调用clearRect( )方法 就可以了: g.clearRect(0, 0, size( ).width, size( ).height);
Graphics类也提供了在屏幕上显示文本的方法,但若要使文本的显示更具特色,让它 满足某种字体、某种风格及尺寸大小的要求,就需要用字体类Font来定义。
当我们想要在屏幕上输出文本信息时,首先要确定的就是采用何种字体,例如中文的 “宋体”、“楷体”,或是英文的“TimesRoman”体、“Courier”体等等,接着再决定 该字体输出时采用哪种风格,是斜体型还是粗体型等等,最后还要确定该字体的大小尺 寸。所有这些都由Font类来定义,我们不难猜出其构造方法的调用格式: Font(String name, int style, int size) 不错,它的三个参数就是我们先前所说的字体名、字体风格和尺寸大小。并且Font类 中已定义了类变量来表示字体的style值,如Font.BOLD(表示粗体)、Font.ITALIC(表示 斜体)、Font.PLAIN(表示普通体)。由于它们被定义为整数常量,因此可以进行相加 运算来生成复合style,例如想让style即是粗体又是斜体,可以这样写: Font fn = new Font("TimesRoman", Font.BOLD+Font.ITALIC, 28); 虽然我们定义了所需的字体,但其显示结果有时也并非如愿。因为运行该applet的客 户端系统有可能并未安装该字体,这时Java就会以缺省字体来替代它。因此,不妨先查看 一下客户端系统目前究竟支持哪些字体,这就要用到java.awt.Toolkit类中的getFontlist( ) 方法,它返回系统目前可用的字体列表,然后就可决定到底选用哪种字体。例如: Toolkit systk = Toolkit.getDefaultToolkit( ); String fonts = systk.getFontList( );
创建了Font对象以后,我们就可以利用Graphics类中提供的drawString( )、drawChars( ) 等方法来显示字符串与字符。当然,首先还要用setFont( )方法,将所创建的Font对象设 为当前所用的字体。下面就是Graphics类中这三个方法的调用格式: setFont(Font font); drawString(String str, int x, int y) drawChars(char data[], int offset, int length, int x, int y) 其中setFont( )方法的参数就是一个创建好的Font对象,表明系统当前选用哪个Font 对象所定义的字体信息。drawString( )方法中的str即是要显示的字符串,x,y指明字符 串显示的起始位置坐标,具体的说,x表示第一个字符的左边界,y表示整个字符串的基线 (baseline,见图4-12)位置坐标。因此,这里的坐标并不是通常意义上的矩形区域的左 上角。drawChars( )方法则是用来显示多个字符的,也就是从给定的字符数组中抽取连续 的一部分显示在屏幕上。其中data参数就是给定的原始字符数组,offset表示从第几个字 符位置开始显示,length表示共显示几个字符,x与y参数的含义与drawString( )方法一样, 代表显示在屏幕上的起始位置。 如下面的程序显示了一些不同的文本字体,其显示结果如图4-11所示。 import java.awt.Graphics; import java.awt.Font; public class Fonts extends java.applet.Applet{ public void paint(Graphics g){ Font ftp20 = new Font("TimesRoman",Font.PLAIN,20); Font fai15 = new Font("Arial",Font.ITALIC,15); Font fcb24 = new Font("Courier",Font.BOLD,24); Font fsib30 = new Font("宋体",Font.ITALIC+Font.BOLD,30); g.setFont(ftp20); g.drawString("Font name TimesRoman , style plain , size 20",10,20); g.setFont(fai15); g.drawString("Font name Arial , style italic , size 15",10,50); g.setFont(fcb24); g.drawString("Font name Courier , style bold , size 24",10,80); g.setFont(fsib30); g.drawString("字体名 宋体,风格 斜体+粗体,尺寸 30",10,120); } }
图4-11 各种字体的例子
(1)获取基本信息 如果不清楚系统当前所用的字体信息,可以先调用Graphics类中的getFont( )方法, 该方法无参数,它返回系统当前所用的Font对象,然后就可以调用Font类中提供的几个方 法来获取该字体的基本信息。表4-1列出了Font类中的这些方法。 表4-1 Font类提供的一些主要方法 (2)获取详细信息 有时候,我们为了在屏幕上更精确地定位文本,还需要了解所选字体的更详细的信息 ,例如整个字符串到底有多高,有多宽,两行字符串的间隙有多少等等。这时,我们需要 用到一个新的类FontMetrics来提供这一信息。我们可以调用Graphics类中的getFontMetrics( ) 方法来获取关于当前字体的FontMetrics对象(该方法也无参数)。然后就可以利用表4-2 所示的FontMetrics类中所提供的方法来获取更详细的字体信息。 图4-12中给出了字体中关于Ascent、Descent、Leading等概念的示意图。
现在,让我们改变一下总是在灰色背景上用黑色绘图以及显示文本的习惯,而给我们 的applet增添一些五彩缤纷的色彩。与设置字体信息相似,要设置新的颜色,必须先创建 Color对象,然后再调用Graphics类中设置颜色的方法来将生成的Color对象设为当前所用 的绘图颜色。
图4-12字体
Java中每一种颜色都看成是由红(R)、绿(G)、蓝(B)三原色组合而成的。因此 Color类的构造方法采用如下格式: Color(int r, int g, int b) 其中每个参数的值都在0到255之间,数值越大就表明这种颜色的成份越重。例如(0,0,0)? 代表黑色,(255,0,0)代表红色。当然最终在屏幕上是否能显示所定义的颜色还取决于客 户端系统的调色板所支持的颜色种类的多少。若客户端系统的调色板并不支持当前所定义 的颜色值,就会在调色板中挑选最接近的颜色来代替。 Color类中还定义了一些标准颜色的Color对象存储在类变量中,使的这些标准颜色的 引用显得更为方便。这些类变量如表4-3所示。
为了能使用刚才生成好的Color对象来显示文本及绘制图形,还需调用Graphics类中的