|
视频课堂:https://edu.csdn.net/course/play/8222
AWT根据类的层次定义窗口,并在每一层添加了特定的功能。在这些窗口中,用得最普遍的是在小应用程序派生于Panel类的窗口和派生于Frame类的独立窗口。这些窗口的功能大多数来自于它们的父类。因此,与Panel和Frame这两个类相关的类结构的描述是我们理解它们的基础。在图11-1中展示了Panel和Frame类的结构。现在让我们分别来看一下这些类。
|
在AWT类层次结构的顶部是Component类。 Component类是一个封装了一个可视组件的所有属性的抽象类。在屏幕上显示的所有用于用户交互的用户界面元素都是Component类的子类。这个类定义了一百多个用于事件管理的公共方法,这些事件包括鼠标或键盘的输入,窗口位置或大小的改变以及重绘窗口。一个Component对象可以保存当前的前景色、背景色以及被选择的文本的字体。
图11-1 Panel和 Frame的类层次结构
|
Container类是Component类的子类。这个类有一些附加方法,允许别的Component对象嵌套在Container类的对象中。当然,其他的Container对象可以被存放在一个Container对象中(因为它们也是Component类的实例)。这就形成了一个多层包容机制。容器主要负责布置它所包含的组件的位置。而它是通过使用一些设计管理器来完成这个功能的,你将在第22章中学习这些设计管理器。
|
Panel类是Container类的一个具体的子类。它没有添加任何新的方法;它只是简单的实现了Container类。一个Panel对象可以被看作是一个递归嵌套的具体的屏幕组件。Panel类是Applet类的子类。当屏幕输出直接传递给一个小应用程序时,它将在一个Panel对象的表面被画出。实际上,一个Panel对象是一个不包含标题栏、菜单栏以及边框的窗口。这就是为什么在浏览器中运行一个小应用程序时,你看不见标题栏、菜单栏以及边框的原因。而当你用小应用程序查看器来运行一个小应用程序时,小应用程序查看器提供了标题和边框。其他的组件可以通过调用Panel类的add()方法被加入到一个Panel对象中,这个方法是从Container类继承来的。一旦这些组件被加入,那么你通常就可以通过调用在Component类中定义了的setLocation( ), setSize( )以及setBounds( )方法来改变这些组件的位置和大小。
|
窗口类产生一个顶级窗口(Window)。顶级窗口不包含在任何别的对象中,它直接出现在桌面上。通常,你将不会直接产生Window对象。相反,你将使用Window类的子类,这就是Frame类。
|
Frame类封装了窗口通常所需要的一切组件,它是Window类的子类,并且拥有标题栏、菜单栏、边框以及可以调整大小的角。如果你在一个小应用程序中创建了一个Frame对象,它将包含一个例如“Java Applet Window”的警告消息给用户,表示一个小应用程序窗口已经被创建。这个消息警告用户,他们看见的窗口是由小应用程序启动的,而不是被运行在他们机器上的软件所启动(一个伪装基于主机的应用程序的小应用程序将可以用于在用户不知道的情况下获得密码和其他敏感信息)。当一个Frame窗口被程序而不是小应用程序创建时,就创建了一个通常的窗口。
|
虽然画布不是小应用程序和frame窗口的层次结构的一部分,但是Canvas这种类型的窗口是很有用的。Canvas类封装了一个你可以用来绘制的空白窗口。你将在这本书的后面看到一个有关Canvas的例子。
|
在小应用程序之中,你最常创建的窗口来自于Frame类。你将用它在小应用程序中创建子窗口,在应用程序中创建顶级或子窗口。正如前面所提到的那样,它会生成一个标准样式的窗口。
Frame的构造函数如下所示:
Frame( )
Frame(String title)
第一种形式用于创建一个不含标题的标准窗口。第二种形式用于创建一个含有标题的窗口,这个标题是由title变量指定的。请注意你不能在创建时指定窗口的大小,你必须在窗口被创建后再设置窗口的大小。
这里有几个方法在你使用Frame窗口时将会用到。下面我们举例说明。
|
setSize( )这个方法用来设置窗口的大小,如下所示:
void setSize(int newWidth, int newHeight)
void setSize(Dimension newSize)
窗口的新的大小在变量newWidth和newHeight中被指定,或者在来自Dimension类的newSize对象的width和height这两个成员变量中被指定。这些大小使用像素为单位。
getSize( )这个方法被用来获得当前的窗口大小,如下所示:
Dimension getSize( )
这个方法返回一个Dimension对象,在这个对象的成员变量width和height中存放着当前窗口的大小。
|
当一个frame窗口被创建以后,这个窗口默认是不可见的,除非你调用它的setVisible()方法。如下所示:
void setVisible(boolean visibleFlag)
如果这个方法的参数是true,那么调用它的组件是可见的。否则,就被隐藏。
|
你可以通过使用setTitle( )方法来改变一个frame窗口的标题。如下所示:
void setTitle(String newTitle)
在这里,参数newtitle是窗口的新标题。
|
当使用一个frame窗口时,你的程序必须在它被关闭时通过调用setVisible(false)方法来将窗口从屏幕中除去。为了截获窗口关闭事件,你必须实现WindowListener监听器接口的windowClosing( )方法。在windowClosing( )方法中,你必须将窗口从屏幕中除去。在下一节中将用一个例子说明这种技术。
|
简单地通过创建一个frame类的实例来创建一个窗口是可能的,但是你可能很少会这样做,因为对于这样的窗口你没有什么可以做的。例如,你将不能接受和处理在这个窗口中发生的事件或者不能简单的输出信息给它。大多数情况下,你将创建一个frame类的子类。这样做,你将会重载frame类的方法和事件处理。
在小应用程序中创建一个新的基于frame的窗口是很容易的。首先,创建一个frame类的子类。接下来,重载任何一个标准窗口方法,比如init( )方法,start( )方法,stop( )方法和paint( )方法。最后,实现windowListener这个监听器接口的windowClosing( )方法,在这个方法中,当窗口被关闭时,调用setVisible(false)方法将窗口从屏幕中除去。
一旦你已经定义了一个frame类的子类,你就可以创建这个类的对象了。这就会产生一个基于frame的窗口,但是它在初始化中被设置为不可见的。你可以通过调用setVisible()方法来使它可见。当这个窗口被创建后,它就有了一个默认的高度和宽度。你可以通过调用setSize( )方法来显式改变窗口的大小。
下面的小应用程序创建了一个叫做SampleFrame的Frame类的子类。这个子类的窗口在AppletFrame类的init()方法中实例化。请注意SampleFrame类调用了Frame类的构造函数。这将创建一个标准的frame窗口,它的标题由title参数决定。这个例子重载了小应用程序窗口的start()方法和stop()方法,所以在这两个方法中相应地显示和隐藏子窗口。这样当你在结束小应用程序时,关闭窗口,或者在浏览器中转移到另一页时,窗口都可以自动地被移去。而当你重新回到小应用程序时子窗口又被显示了出来。
// Create a childframe window from within an applet. import java.awt.*; import java.awt.event.*; import java.applet.*; /* */ // Create a subclass of Frame. class SampleFrame extends Frame { SampleFrame(String title) { super(title); // create anobject to handle window events MyWindowAdapteradapter = new MyWindowAdapter(this); // register itto receive those events addWindowListener(adapter); } public voidpaint(Graphics g) { g.drawString("Thisis in frame window", 10, 40); } } class MyWindowAdapter extends WindowAdapter { SampleFramesampleFrame; publicMyWindowAdapter(SampleFrame sampleFrame) { this.sampleFrame = sampleFrame; } public voidwindowClosing(WindowEvent we) { sampleFrame.setVisible(false); } } // Create frame window. public class AppletFrame extends Applet { Frame f; public voidinit() { f = newSampleFrame("A Frame Window"); f.setSize(250,250); f.setVisible(true); } public voidstart() { f.setVisible(true); } public voidstop() { f.setVisible(false); } public voidpaint(Graphics g) { g.drawString("This is in applet window", 10, 20); } }
这个例子的输出如下所示:
|
由于Frame类是Component类的子类,所以它继承了Component类的所有能力。这就意 味着你可以像管理小应用程序主窗口一样地使用和管理frame窗口。例如, 你可以重载paint()方法来显示输出, 也可以在你需要恢复窗口时调用repaint()方法并且还可以重载所有的事件处理程序。无论何时,一个事件在窗口中发生时,由这个窗口定义的事件处理方法将被调用。每一个窗口都处理自己的事件。例如,接下来的程序创建了一个响应鼠标事件的窗口。
小应用程序的主窗口中也响应鼠标事件。当你运行这个程序时,你将看到鼠标事件被发送给了那个产生该事件的窗口。
// Handle mouse events in both child and applet windows. import java.awt.*; import java.awt.event.*; import java.applet.*; /* */ // Create a subclass of Frame. class SampleFrame extends Frame implementsMouseListener, MouseMotionListener { String msg =""; int mouseX=10,mouseY=40; int movX=0,movY=0; SampleFrame(String title) { super(title); // registerthis object to receive its own mouse events addMouseListener(this); addMouseMotionListener(this); // create anobject to handle window events MyWindowAdapteradapter = new MyWindowAdapter(this); // register itto receive those events addWindowListener(adapter); } // Handle mouseclicked. public voidmouseClicked(MouseEvent me) { } // Handle mouseentered. public voidmouseEntered(MouseEvent evtObj) { // savecoordinates mouseX = 10; mouseY = 54; msg ="Mouse just entered child."; repaint(); } // Handle mouseexited. public voidmouseExited(MouseEvent evtObj) { // savecoordinates mouseX = 10; mouseY = 54; msg ="Mouse just left child window."; repaint(); } // Handle mousepressed. public voidmousePressed(MouseEvent me) { // savecoordinates mouseX =me.getX(); mouseY =me.getY(); msg ="Down"; repaint(); } // Handle mousereleased. public voidmouseReleased(MouseEvent me) { // savecoordinates mouseX =me.getX(); mouseY =me.getY(); msg ="Up"; repaint(); } // Handle mousedragged. public voidmouseDragged(MouseEvent me) { // savecoordinates mouseX =me.getX(); mouseY =me.getY(); movX =me.getX(); movY =me.getY(); msg ="*"; repaint(); } // Handle mousemoved. public voidmouseMoved(MouseEvent me) { // savecoordinates movX =me.getX(); movY =me.getY(); repaint(0, 0,100, 60); } public voidpaint(Graphics g) { g.drawString(msg, mouseX, mouseY); g.drawString("Mouse at " + movX + ", " + movY, 10,40); } } class MyWindowAdapter extends WindowAdapter { SampleFramesampleFrame; publicMyWindowAdapter(SampleFrame sampleFrame) { this.sampleFrame = sampleFrame; } public voidwindowClosing(WindowEvent we) { sampleFrame.setVisible(false); } } // Applet window. public class WindowEvents extends Applet implementsMouseListener, MouseMotionListener { SampleFrame f; String msg =""; int mouseX=0,mouseY=10; int movX=0,movY=0; // Create a framewindow. public voidinit() { f = newSampleFrame("Handle Mouse Events"); f.setSize(300,200); f.setVisible(true); // registerthis object to receive its own mouse events addMouseListener(this); addMouseMotionListener(this); } // Remove framewindow when stopping applet. public voidstop() { f.setVisible(false); } // Show framewindow when starting applet. public voidstart() { f.setVisible(true); } // Handle mouseclicked. public voidmouseClicked(MouseEvent me) { } // Handle mouseentered. public voidmouseEntered(MouseEvent me) { // savecoordinates mouseX = 0; mouseY = 24; msg ="Mouse just entered applet window."; repaint(); } // Handle mouseexited. public voidmouseExited(MouseEvent me) { // savecoordinates mouseX = 0; mouseY = 24; msg ="Mouse just left applet window."; repaint(); } // Handle buttonpressed. public voidmousePressed(MouseEvent me) { // savecoordinates mouseX =me.getX(); mouseY =me.getY(); msg ="Down"; repaint(); } // Handle buttonreleased. public voidmouseReleased(MouseEvent me) { // savecoordinates mouseX =me.getX(); mouseY =me.getY(); msg ="Up"; repaint(); } // Handle mousedragged. public voidmouseDragged(MouseEvent me) { // savecoordinates mouseX =me.getX(); mouseY =me.getY(); movX =me.getX(); movY =me.getY(); msg ="*"; repaint(); } // Handle mousemoved. public voidmouseMoved(MouseEvent me) { // save coordinates movX =me.getX(); movY =me.getY(); repaint(0, 0,100, 20); } // Display msg inapplet window. public voidpaint(Graphics g) { g.drawString(msg, mouseX, mouseY); g.drawString("Mouse at " + movX + ", " + movY, 0,10); } }
这个程序的输出如下所示:
|
虽然Java的AWT常用来创建小应用程序,但是它也一样可以被用来创建独立的基于AWT的应用程序。这可以通过在main()方法中简单的创建一个你需要的窗口实例或窗口来实现。例如,接下来的程序创建了frame窗口,它可以响应鼠标点击和键盘击键。
// Create an AWT-based application. import java.awt.*; import java.awt.event.*; import java.applet.*; // Create a frame window. public class AppWindow extends Frame { String keymsg =""; String mousemsg =""; int mouseX=30,mouseY=30; publicAppWindow() { addKeyListener(new MyKeyAdapter(this)); addMouseListener(new MyMouseAdapter(this)); addWindowListener(new MyWindowAdapter()); } public voidpaint(Graphics g) { g.drawString(keymsg, 10, 40); g.drawString(mousemsg, mouseX, mouseY); } // Create thewindow. public staticvoid main(String args[]) { AppWindowappwin = new AppWindow(); appwin.setSize(new Dimension(300, 200)); appwin.setTitle("An AWT-Based Application"); appwin.setVisible(true); } } class MyKeyAdapter extends KeyAdapter { AppWindowappWindow; publicMyKeyAdapter(AppWindow appWindow) { this.appWindow= appWindow; } public voidkeyTyped(KeyEvent ke) { appWindow.keymsg += ke.getKeyChar(); appWindow.repaint(); }; } class MyMouseAdapter extends MouseAdapter { AppWindowappWindow; public MyMouseAdapter(AppWindowappWindow) { this.appWindow= appWindow; } public voidmousePressed(MouseEvent me) { appWindow.mouseX = me.getX(); appWindow.mouseY = me.getY(); appWindow.mousemsg = "Mouse Down at " + appWindow.mouseX + ", " + appWindow.mouseY; appWindow.repaint(); } } class MyWindowAdapter extends WindowAdapter { public voidwindowClosing(WindowEvent we) { System.exit(0); } }
这个程序的输出如下所示:
一旦被创建,frame窗口就有了自己的生命特征。请注意main()方法结束时调用了appwin.setVisible(true)方法。所以,无论怎样,在关闭窗口之前这个程序将一直保持运行状态。事实上,当创建一个基于窗口的应用程序时,你将用main()方法来启动它的顶级窗口。
在此之后,你的程序将作为一个基于GUI的应用程序运行,而不是开始时的基于控制台的应用程序。
|
在大多数情况下,一个窗口是一个信息的容器。虽然我们在前面的例子中已经输出了一些文本到窗口中, 但是我们还没有开始利用一个窗口的优势去显示高质量的文本和图像。
实际上,AWT的许多功能来自于对它们的支持。在本章剩余部分,我们将讨论Java的文字、图形和字体处理能力。正如你将看到的那样,它们不但功能强大而且很灵活。
|
AWT支持图形方法。所有的图形被画到相关联的窗口中,而这个相关联的窗口可能是一个小应用程序的主窗口,也可能是一个小应用程序的子窗口,或者是一个独立应用程序的窗口。每一个窗口的原点都位于窗口的左上角,以像素为单位坐标为(0,0)的点。在这个窗口里,所有的输出都是通过一个图形上下文(graphics context)来产生的。图形上下文是由Graphics类封装的,它可以通过两种方法获得:
·当某一方法如paint( )和update( )被调用时,它被传递给小应用程序。
·它可以由Component类的getGraphics( )方法返回。
在本章剩下的例子中,我们将在一个小应用程序的主窗口中示范图形。不过,请注意,这些技术也可以被用到别的窗口中。
Graphics类定义了一些绘图函数。每一个图形都可以只画边框或者被填充。这些对象用当前选择的颜色来绘制和填充,黑色是默认的颜色。当一个被绘制图形对象的尺寸超过了窗口的大小时,超出的那部分输出将会自动被剪去。让我们来看几个绘图方法吧。
方法如下所示:
void drawLine(int startX, int startY, int endX, int endY)
|
通过drawLine( )方法我们可以画线,语(startX,startY)为起点,(endX,endY)为终点画一条直线。
接下来的这个小应用程序演示了如何画线。
// Draw lines import java.awt.*; import java.applet.*; /* */ public class Lines extends Applet { public voidpaint(Graphics g) { g.drawLine(0,0, 100, 100); g.drawLine(0,100, 100, 0); g.drawLine(40,25, 250, 180); g.drawLine(75, 90,400, 400); g.drawLine(20,150, 400, 40); g.drawLine(5,290, 80, 19); } }
这个例子的输出如下所示:
|
drawRect( )方法和fillRect()方法分别可以用来绘制一个矩形的轮廓和一个被填充的矩形。语法如下所示:
void drawRect(int top, int left, int width, int height)
void fillRect(int top, int left, int width, int height)
矩形的左上角在(top,left),矩形的大小由参数width和heigh来确定。
为了绘制一个圆角矩形,可以用drawRoundRect( )方法或者fillRoundRect( )方法,语法如下所示:
void drawRoundRect(int top, int left, int width, intheight,
intxDiam, int yDiam)
void fillRoundRect(int top, int left, int width, intheight,
int xDiam, int yDiam)
一个圆角矩形的角是圆的。这个矩形的左上角是在(top,left)。这个矩形的大小有参数width和height来确定。 X方向圆弧的直径由参数xDiam确定。 Y方向圆弧的直径由参数yDiam确定。下面的例子演示了如何画这些矩形。
// Draw rectangles import java.awt.*; import java.applet.*; /* */ public class Rectangles extends Applet { public voidpaint(Graphics g) { g.drawRect(10,10, 60, 50); g.fillRect(100,10, 60, 50); g.drawRoundRect(190, 10, 60, 50, 15, 15); g.fillRoundRect(70, 90, 140, 100, 30, 40); } }
程序输出如下所示:
|
用drawOval( )方法可以绘制一个椭圆。而用fillOval( )方法可以填充一个椭圆。这些方法的语法如下所示:
void drawOval(int top, int left, int width, int height)
void fillOval(int top, int left, int width, int height)
椭圆被绘制在一个矩形范围内,这个矩形的左上角是(top,left),而大小由参数width和height确定。绘制圆形时,我们只需指定矩形为一个正方形。
接下来的例子演示了如何绘制椭圆。
// Draw Ellipses import java.awt.*; import java.applet.*; /* */ public class Ellipses extends Applet { public voidpaint(Graphics g) { g.drawOval(10,10, 50, 50); g.fillOval(100,10, 75, 50); g.drawOval(190,10, 90, 30); g.fillOval(70,90, 140, 100); } }
这个例子的输出如下所示:
|
通过drawArc( )方法和fillArc()方法我们可以绘制圆弧。它们的原型如下所示:
void drawArc(int top, int left, int width, int height,int startAngle, int sweepAngle)
void fillArc(int top, int left, int width, int height,int startAngle, int sweepAngle)
圆弧被绘制在一个矩形范围内,这个矩形的左上角是(top,left)点,而大小由参数width和height确定。圆弧是以startAngle为开始的角度,sweepAngle为转过的角度而绘制的。这些角是以度为单位的。 0度指水平方向上,类似钟表上三点钟的时针位置。 如果参数sweepAngle是正的,圆弧将被逆时针绘制,否则将被顺时针绘制。因此,为了画出一个从12点到6点的圆弧,我们应该设置开始的角度为90°而转过的角度为180°。
接下来的例子演示了如何绘制圆弧。
// Draw Arcs import java.awt.*; import java.applet.*; /* */ public class Arcs extends Applet { public voidpaint(Graphics g) { g.drawArc(10,40, 70, 70, 0, 75); g.fillArc(100,40, 70, 70, 0, 75); g.drawArc(10,100, 70, 80, 0, 175); g.fillArc(100,100, 70, 90, 0, 270); g.drawArc(200,80, 80, 80, 0, 180); } }
这个例子的输出如下所示:
|
通过使用drawPolygon( )方法和fillPolygon()方法,我们可以绘制出任意的形状,这些方法的语法如下所示:
void drawPolygon(int x[ ], int y[ ], int numPoints)
void fillPolygon(int x[ ], int y[ ], int numPoints)
多边形的顶点是由数组x和数组y中相对应的数字组成的坐标来指定的。而数组x,y中定义的点的个数是由参数numPoints确定的。这些方法的另一种形式是通过ploygon对象来指定多边形。
接下来的例子演示了如何绘制一个沙漏的形状。
// Draw Polygon import java.awt.*; import java.applet.*; /* */ public class HourGlass extends Applet { public voidpaint(Graphics g) { int xpoints[] ={30, 200, 30, 200, 30}; int ypoints[] ={30, 30, 200, 200, 30}; int num = 5; g.drawPolygon(xpoints, ypoints, num); } }
这个例子的输出如下所示:
|
我们经常想去改变一个图形对象,以便它的大小与它所在的窗口的大小匹配。为此,我们首先要通过窗口对象的getSize( )方法获得当前窗口的尺寸。这个方法返回窗口的尺寸并封装在Dimension对象中。一旦你获得当前窗口的尺寸,便可以相应的调整你的图形输出的大小。
为了演示这个技术,我们让一个小应用程序开始时在200×200的像素范围内,并且每用鼠标点击一次,小应用程序的范围的长和宽分别会增加25个像素点,直到小应用程序的范围大于500×500的像素范围。将不断接下来的点击又使小应用程序的范围不断减小,直至回到了开始时200×200的像素范围,这一过程将不断重复。在小应用程序的窗口中,沿着窗口的内边框绘制了一个矩形,在矩形的内部还绘制了一个X,填满整个窗口。这个小应用程序要在appletviewer中才能工作,在浏览器中它不运行。
// Resizing output to fit the current size of a window. import java.applet.*; import java.awt.*; import java.awt.event.*; /* */ public class ResizeMe extends Applet { final int inc =25; int max = 500; int min = 200; Dimension d; public ResizeMe() { addMouseListener(new MouseAdapter() { public voidmouseReleased(MouseEvent me) { int w =(d.width + inc) > max?min :(d.width + inc); int h =(d.height + inc) > max?min :(d.height + inc); setSize(newDimension(w, h)); } }); } public voidpaint(Graphics g) { d = getSize(); g.drawLine(0,0, d.width-1, d.height-1); g.drawLine(0,d.height-1, d.width-1, 0); g.drawRect(0,0, d.width-1, d.height-1); } }
|
Java以一种方便、设备无关的方式支持颜色。AWT的颜色系统允许你指定任何你需要的颜色。在执行程序或小应用程序时,虽然受到了显示硬件的限制,但是它将找到与之最相近的颜色。因此,编写代码时无需关心由于不同的硬件设备所支持的方式不同而引起的颜色差别。颜色被封装在Color类中。 正如你在第19章中所看到的,Color类定义了一些常量(比如,Color.black)来指定一些常用的颜色。你也可以通过Color类的构造函数来生产你自己的颜色。这些方法的形式如下所示:
Color(int red, int green, int blue)
Color(int rgbValue)
Color(float red, float green, float blue)
第一个构造函数使用了三个分别代表红、绿、蓝的整数来表示它们混合的颜色。这些值像下面这个例子中的一样,必须在0~255之间。
new Color(255, 100, 100); // light red.
第二个构造函数采用一个由红、绿、蓝按照一定的格式压缩成的整数来表示颜色。这个整数的0-7位代表蓝,8-15位代表绿,16-23位代表红。这里是一个使用这个构造函数的例子:
int newRed = (0xff000000 | (0xc0 << 16) | (0x00<< 8) | 0x00);
Color darkRed = new Color(newRed);
最后一个构造函数Color(float, float, float), 用了三个浮点数指定红、绿、 蓝的相对混合。
一旦你已经生成了一个颜色,你将可以通过调用在19章中描述过的setForeground( )方法和setBackground( )方法使它作为前景色或者背景色。你也可以用该颜色来作为当前的绘图颜色。
|
Color类定义了几种使用颜色的方法。下面我们将讨论它们。
使用色相、饱和度、亮度 HSB(hue-saturation-brightness)颜色模型是除了RGB模型外的另一种可以用来指定特定颜色的方式。其中,色相好比是颜色的轮子,是由一个0.0~1.0之间的数来确定的(颜色大约有:红色,橙色,黄色,绿色,蓝色,靛青,紫色)。饱和度是另一个介于0.0~1.0之间的值,它代表了相应的色相的深浅或鲜艳程度。亮度也是一个介于0.0~1.0之间的值,当为1时表示明亮,而0表示黑暗。Color类支持两个方法,它们可以让你在RGB和HSB之间进行转换。它们的原型如下所示:
static int HSBtoRGB(float hue, float saturation, floatbrightness)
static float[ ] RGBtoHSB(int red, int green, int blue,float values[ ])
HSBtoRGB( )方法返回了一个与构造函数Color(int)兼容的被压缩的RGB值。
RGBtoHSB( )方法返回了一个与RGB值相应的HSB值的浮点数组。如果values不是null,那么这个数组返回的是HSB值;否则,产生一个新的数组并且在里面放着HSB的值。在数组中,色相的下标为0,饱和度的下标为1,亮度的下标为2。
getRed( ), getGreen( ), getBlue( )
你可以通过调用getRed( )方法、getGreen()方法和getBlue( )方法来获得一个颜色中包含的红、绿、蓝的成份。语法如下所示:
int getRed( )
int getGreen( )
int getBlue( )
这些方法中的每一个都返回在低8位整数中Color对象的RGB颜色中的相应的成份。
getRGB( )
为了获得一个颜色的RGB值,我们可以调用getRGB()方法,格式如下所示:
int getRGB( )
这个方法的返回值和前面所描述的一样。
|
默认情况下,图形对象是用当前的前景色来绘制的。你可以改变这个颜色,这是通过调用Graphics类的setColor( )方法来实现的:
void setColor(Color newColor)
这里,newcolor指定了新的绘图颜色。
你也可以通过调用getColor( )方法来获得当前的颜色,格式如下所示:
Color getColor( )
|
接下来的这个小应用程序创建了几种颜色,并且用这些颜色绘制几个图形对象。
// Demonstrate color. import java.awt.*; import java.applet.*; /* */ public class ColorDemo extends Applet { // draw lines public voidpaint(Graphics g) { Color c1 = newColor(255, 100, 100); Color c2 = newColor(100, 255, 100); Color c3 = newColor(100, 100, 255); g.setColor(c1); g.drawLine(0,0, 100, 100); g.drawLine(0,100, 100, 0); g.setColor(c2); g.drawLine(40,25, 250, 180); g.drawLine(75,90, 400, 400); g.setColor(c3); g.drawLine(20,150, 400, 40); g.drawLine(5,290, 80, 19); g.setColor(Color.red); g.drawOval(10,10, 50, 50); g.fillOval(70,90, 140, 100); g.setColor(Color.blue); g.drawOval(190,10, 90, 30); g.drawRect(10,10, 60, 50); g.setColor(Color.cyan); g.fillRect(100,10, 60, 50); g.drawRoundRect(190, 10, 60, 50, 15, 15); } }
|
绘图模式(paint mode)决定了对象是如何被画在窗口中的。默认情况下,对一个窗口的新的输出将覆盖该窗口中任何已经存在的内容。然而,通过调用setXORMode( )方法设置绘图模式,我们可以使一个新的对象以异或操作的方式加入到窗口中,语法如下所示:
void setXORMode(Color xorColor)
在这里,xorColor指定的是绘制对象时与窗口进行异或操作的颜色。异或模式的优点是新的对象总是可以保证被看见,无论这个对象是用什么颜色画的。
如果你想回到覆盖模式,那么你可以调用setPaintMode( )方法,语法如下所示:
void setPaintMode( )
通常,你将希望对一般的输出用覆盖模式,而在特定的情况下采用异或模式。例如,接下来的程序中显示了一个跟踪鼠标指针的十字。这个十字被异或到窗口中,所以无论指针下面是什么颜色它都总是可见的。
// Demonstrate XOR mode. import java.awt.*; import java.awt.event.*; import java.applet.*; /* */ public class XOR extends Applet { int chsX=100,chsY=100; public XOR() { addMouseMotionListener(new MouseMotionAdapter() { public voidmouseMoved(MouseEvent me) { int x = me.getX(); int y =me.getY(); chsX = x-10; chsY =y-10; repaint(); } }); }
public voidpaint(Graphics g) { g.drawLine(0, 0, 100, 100); g.drawLine(0,100, 100, 0); g.setColor(Color.blue); g.drawLine(40,25, 250, 180); g.drawLine(75,90, 400, 400); g.setColor(Color.green); g.drawRect(10,10, 60, 50); g.fillRect(100,10, 60, 50); g.setColor(Color.red); g.drawRoundRect(190, 10, 60, 50, 15, 15); g.fillRoundRect(70, 90, 140, 100, 30, 40); g.setColor(Color.cyan); g.drawLine(20,150, 400, 40); g.drawLine(5,290, 80, 19); // xor crosshairs g.setXORMode(Color.black); g.drawLine(chsX-10, chsY, chsX+10, chsY); g.drawLine(chsX, chsY-10, chsX, chsY+10); g.setPaintMode(); } }
这个例子的输出如下所示:
|
AWT支持多种字体。字体已从传统的排版领域发展为产生计算机文档和显示的重要部分。AWT通过提炼字体处理提供了灵活的操作,并且允许字体的动态选择。
从Java 2开始,字体有了一个姓名、一个逻辑字体名和一个外形名。姓名(family name)是字体最通常的名字, 比如courier。 逻辑名(logical name) 指定了一类字体, 比如monospaced。而外形名(face name)指定了一个特定的字体,比如courier italic。字体被封装在font类中。在表21-2中,列出了一些font类定义的方法。
一些font 定义的方法
Font类定义了以下变量:
|
当使用字体时,你经常需要知道哪一种字体在你的机器上是可用的。为了获得这些信息,你可以调用在GraphicsEnvironment类中定义的getAvailableFontFamilyNames( )方法。如下所示:
String[ ] getAvailableFontFamilyNames( )
这个方法返回了一个字符串数组,它包含了可以使用的字体集合的名字。
除此之外,在GraphicsEnvironment类中定义的getAllFonts( )方法也可以完成这个功能。语法如下所示:
Font[ ] getAllFonts( )
这个方法返回一个字体对象的数组,这个数组中包含了所有可以使用的字体。 因为这些方法都是类的成员方法,所以你需要用一个GraphicsEnvironment类的引用去调用它们。你可以通过调用getLocalGraphicsEnvironment()这个静态方法来获得一个GraphicsEnvironment类的引用,这个方法被定义在GraphicsEnvironment类中,如下所示:
static GraphicsEnvironmentgetLocalGraphicsEnvironment( )
下面的这个小应用程序演示了如何获得可用的字体集合的名字。
// Display Fonts /* */ import java.applet.*; import java.awt.*; public class ShowFonts extends Applet { public voidpaint(Graphics g) { String msg =""; String FontList[]; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); FontList =ge.getAvailableFontFamilyNames(); for(int i = 0;i < FontList.length; i++) msg +=FontList[i] + " "; g.drawString(msg, 4, 16); } }
这个例子的输出如下图所示。然而,当你运行这个程序时,你可能看到不同于图中显示的字体的列表。
注意:在Java 2之前,需要使用在Toolkit类中定义的getFontList( )方法来获得字体的列表。现在已经反对再使用这个方法,所以不应该在新的程序中使用它。
|
为了选择一个新的字体,你必须首先创建一个描述了字体的Font对象。Font类的构造函数的一种形式如下所示:
Font(String fontName, int fontStyle, int pointSize)
在这里,参数fontname指定了希望使用的字体的名字。这个名字既可以是逻辑名也可以是外形名。所有的Java环境都将支持如下的字体:Dialog, DialogInput, SansSerif, Serif, Monospaced, 以及 Symbol。Dialog是你的系统对话框使用的字体。如果你不明确的指定字体,Dialog也是默认的字体。你也可以用任何其他被你的特殊环境支持的字体,但是请小心,这些字体未必都是可用的。
字体的样式是由参数fontstyle指定的。它可以由Font.PLAIN,Font.BOLD以及Font.ITALIC这三个常量中的一个和多个组成。为了结合各种样式,可以把它们用or连接在一起。例如,Font.BOLD | Font.ITALIC指定了一个由粗体和斜体组成的样式。 以磅为单位的字体,大小由pointsize指定。
为了使用你创建的字体,你必须通过调用setFont( )方法来选择该字体,这个方法被定义在Component类中,如下所示:
void setFont(Font fontObj)
在这里,fontObj是包含了你希望使用的字体的对象。
下面的这个程序输出了每一个标准字体的例子。每一次你在窗口中点击鼠标时,一个新的字体将被选择,同时它的名字将被显示出来。
// Show fonts. import java.applet.*; import java.awt.*; import java.awt.event.*; /* */ public class SampleFonts extends Applet { int next = 0; Font f; String msg; public voidinit() { f = newFont("Dialog", Font.PLAIN, 12); msg ="Dialog"; setFont(f); addMouseListener(new MyMouseAdapter(this)); } public voidpaint(Graphics g) { g.drawString(msg,4, 20); } }
class MyMouseAdapter extends MouseAdapter { SampleFontssampleFonts; publicMyMouseAdapter(SampleFonts sampleFonts) { this.sampleFonts = sampleFonts; } public voidmousePressed(MouseEvent me) { // Switch fontswith each mouse click. sampleFonts.next++; switch(sampleFonts.next) { case 0: sampleFonts.f= new Font("Dialog", Font.PLAIN, 12); sampleFonts.msg = "Dialog"; break; case 1: sampleFonts.f= new Font("DialogInput", Font.PLAIN, 12); sampleFonts.msg = "DialogInput"; break; case 2: sampleFonts.f= new Font("SansSerif", Font.PLAIN, 12); sampleFonts.msg = "SansSerif"; break; case 3: sampleFonts.f= new Font("Serif", Font.PLAIN, 12); sampleFonts.msg= "Serif"; break; case 4: sampleFonts.f= new Font("Monospaced", Font.PLAIN, 12); sampleFonts.msg = "Monospaced"; break; } if(sampleFonts.next == 4) sampleFonts.next = -1; sampleFonts.setFont(sampleFonts.f); sampleFonts.repaint(); } }
这个例子的输出如下图所示:
|
如果你想去获得当前被选择的字体的有关信息,必须首先通过调用getFont( )方法来获得当前的字体,这个方法被定义在Graphics类中,如下所示:
Font getFont( )
一旦你获知了当前被选择的字体是哪一种字体,那么你就可以通过调用Font类定义的各种方法来获得它的有关信息了。例如,下面的这个小应用程序显示当前被选择的字体的名字、所属集合、大小以及样式。
// Display font info. import java.applet.*; import java.awt.*; /* */ public class FontInfo extends Applet { public voidpaint(Graphics g) { Font f = g.getFont(); String fontName= f.getName(); String fontFamily = f.getFamily(); int fontSize =f.getSize(); int fontStyle =f.getStyle(); String msg ="Family: " + fontName; msg += ",Font: " + fontFamily; msg += ",Size: " + fontSize + ", Style: "; if((fontStyle& Font.BOLD) == Font.BOLD) msg +="Bold "; if((fontStyle& Font.ITALIC) == Font.ITALIC) msg +="Italic "; if((fontStyle& Font.PLAIN) == Font.PLAIN) msg +="Plain "; g.drawString(msg,4, 16); } }
|
正如我们所期望的一样,Java支持许多字体。对于大部分字体,字符并不具有完全相同的尺寸,许多字体的尺寸是可以调整的。每一个字符的高、字母(descenders)的长度(字母的悬挂部分,例如y)以及行与行之间间距对与每一种字体都是不同的。此外,字体的磅大小也可以被改变。那些可变的属性不是很重要,除非Java要求程序员用手工管理所有的文本输出。
每一种字体的尺寸可能不同,并且在你的程序运行时,字体可能会改变,所以必须有一些方式来决定字体尺寸和当前被选择的字体的其他属性。例如,为了在一行文字之后再写一行就意味着你得知道当前字体的高度和行间距的大小。为了满足这个需要,AWT提供了FontMetrics类,这个类封装了关于字体的一些信息。下面让我们先来了解一些描述字体时常用的术语。
Height 在字体中最高的字符从底部到顶部的距离
Baseline 所有字符的底部所对齐的线
Ascent 从基线到字符顶部的距离
Descent 从基线到字符底部的距离
Leading 一行文字的底部到下一行的顶部的距离
正如你所知道的,我们在前面的许多例子中都使用了drawString( )方法。这个方法用当前的字体和颜色从一个特定的位置开始画出一个字符串。然而,这个位置是这些字符的基线的最左边,而不是像别的一些绘图方法那样在左上角。所以不能把画框的坐标当作是画字符串的坐标。例如,你画了一个矩形在你的小应用程序中的(0,0)点,你将可以看到整个的矩形。但是如果你在同样的坐标处开始画一个字符串“Typesetting”,你将只能看到这些字符中的y,p和g的尾巴。你将看到,通过使用FontMetrics类,你可以为每一个字符串设置合适的显示位置。
FontMetrics定义了几种方法,这些方法可以帮助你管理文字的输出。在下面的表21-3中列出了我们常用的一些方法。这些方法可以帮助你在一个窗口中的适当位置显示文字。
下面让我们来看几个例子。
FontMetrics 定义的一些方法
|
通常,FontMetrics类最常用的功能就是用来决定各行文字之间的距离。还有就是用来决定一个将被显示的字符串的长度。在这里,你将看到如何来实现这些功能的例子。
一般为了显示多行文字,你的程序通常必须不断的跟踪当前输出点的位置。每一次开始一个新行时,Y坐标必须高于下一行的开始点。每当一个字符串被显示之后,这个坐标X必须被设为这个字符串结尾的点。这样就可以使下一个字符串在前一个的结尾写出下。
为了决定行间距, 你可以使用由getLeading( )方法返回的值。 为了决定字体的整体高度,你可以通过由getAscent( )方法返回的值加上由getDescent( )方法返回的值来实现。然后,你可以用这些值确定每一行文本输出的位置。然而,在很多情况下,你将不需要用这些独立的值。当然,所有你需要知道的是一行的整体高度,它是行间距、字体上部分高、字体下部分高的和。获得这个值的最简单的方法是调用getHeight( )方法。在你想开始输出下一行文本时,你需要简单地增加这个值的Y坐标。
为了在同一行中前一部分的结束处开始输出,你必须知道每一个显示的字符串占有多少像素。为了获得这个值,你可以调用stringWidth( )方法。在每次显示一行时,你可以用这个值来决定X坐标。
接下来的这个小应用程序演示了如何在一个窗口中输出多行文字。它也显示了如何在一行中输出多个句子。请注意变量curX和curY,它们用于跟踪当前文本的输出位置。
// Demonstrate multiline output. import java.applet.*; import java.awt.*; /* */ public class MultiLine extends Applet { int curX=0,curY=0; // current position public voidinit() { Font f = new Font("SansSerif",Font.PLAIN, 12); setFont(f); } public voidpaint(Graphics g) { FontMetrics fm= g.getFontMetrics(); nextLine("This is on line one.", g); nextLine("This is on line two.", g); sameLine("This is on same line.", g); sameLine("This, too.", g); nextLine("This is on line three.", g); } // Advance tonext line. voidnextLine(String s, Graphics g) { FontMetrics fm= g.getFontMetrics(); curY +=fm.getHeight(); // advance to next line curX = 0; g.drawString(s,curX, curY); curX =fm.stringWidth(s); // advance to end of line } // Display onsame line. voidsameLine(String s, Graphics g) { FontMetrics fm= g.getFontMetrics(); g.drawString(s,curX, curY); curX +=fm.stringWidth(s); // advance to end of line } }
这个例子的输出如下所示:
|
这里有一个在窗口中文本居中(从左到右,从上到下)的例子。它获得了一个字符串的上半部高,下半部高和宽度,并且计算了使它居中显示的位置。
// Center text. import java.applet.*; import java.awt.*; /* */ public class CenterText extends Applet { final Font f =new Font("SansSerif", Font.BOLD, 18); public voidpaint(Graphics g) { Dimension d =this.getSize(); g.setColor(Color.white); g.fillRect(0,0, d.width,d.height); g.setColor(Color.black); g.setFont(f); drawCenteredString("This is centered.", d.width, d.height, g); g.drawRect(0,0, d.width-1, d.height-1); } public voiddrawCenteredString(String s, int w, int h, Graphics g) { FontMetrics fm= g.getFontMetrics(); int x = (w -fm.stringWidth(s)) / 2; int y =(fm.getAscent() + (h - (fm.getAscent() +fm.getDescent()))/2); g.drawString(s,x, y); } }
这个例子的输出如下所示:
|
如果你曾经使用过文字处理软件,你将已经看到过文字对齐的效果,即一行和多文字的边在一条直线上。例如,许多字处理软件可以左对齐或者是右对齐文本。大多数也可以中间对齐文本。在接下来的这个程序中,你将看到如何实现这些功能。
在这个程序中,要被调整的字符串被分成独立的文字。对于每个字,程序跟踪当前字体的长度,并且在当前行无法全部容纳该字时,自动的移至下一行。所有的输出行都在窗口中用当前选择的对齐样式来显示。每一次你在小应用程序窗口中单击鼠标,对齐样式都会改变。下面的这个例子演示了这些功能。
// Demonstrate text alignment. import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; /* Text Layout quiteeasy. As you haveseen, the AWT provides support for fonts,colors, text, and graphics. Ofcourse, you musteffectively utilize these items if you are toachieve professional results."> */ public classTextLayout extends Applet { final int LEFT =0; final int RIGHT =1; final int CENTER= 2; final intLEFTRIGHT =3; int align; Dimension d; Font f; FontMetrics fm; int fontSize; int fh, bl; int space; String text; public voidinit() { setBackground(Color.white); text =getParameter("text"); try { fontSize =Integer.parseInt(getParameter("fontSize"));} catch(NumberFormatException e) { fontSize=14; } align = LEFT; addMouseListener(new MyMouseAdapter(this)); } public voidpaint(Graphics g) { update(g); } public voidupdate(Graphics g) { d = getSize(); g.setColor(getBackground()); g.fillRect(0,0,d.width, d.height); if(f==null) f =new Font(getParameter("fontname"), Font.PLAIN, fontSize); g.setFont(f); if(fm == null){ fm =g.getFontMetrics(); bl = fm.getAscent(); fh = bl +fm.getDescent(); space =fm.stringWidth(" "); } g.setColor(Color.black); StringTokenizerst = new StringTokenizer(text); int x = 0; int nextx; int y = 0; String word,sp; int wordCount =0; String line =""; while(st.hasMoreTokens()) { word =st.nextToken(); if(word.equals(" ")) { drawString(g, line, wordCount, fm.stringWidth(line), y+bl); line =""; wordCount =0; x = 0; y = y + (fh* 2); } else { int w =fm.stringWidth(word); if(( nextx= (x+space+w)) > d.width ) { drawString(g, line, wordCount, fm.stringWidth(line), y+bl); line =""; wordCount= 0; x = 0; y = y +fh; } if(x!=0){sp = " ";} else {sp = "";} line = line+ sp + word; x = x +space + w; wordCount++; } } drawString(g,line, wordCount, fm.stringWidth(line), y+bl); } public voiddrawString(Graphics g, String line, int wc, int lineW, int y) { switch(align) { case LEFT:g.drawString(line, 0, y); break; case RIGHT:g.drawString(line, d.width-lineW ,y); break; case CENTER:g.drawString(line, (d.width-lineW)/2, y); break; caseLEFTRIGHT: if(lineW< (int)(d.width*.75)) { g.drawString(line, 0, y); } else { inttoFill = (int)((d.width - lineW)/wc); int nudge= d.width - lineW - (toFill*wc); int s =fm.stringWidth(" "); StringTokenizer st = new StringTokenizer(line); int x =0; while(st.hasMoreTokens()) { Stringword = st.nextToken(); g.drawString(word, x, y); if(nudge>0) { x = x+ fm.stringWidth(word) + space + toFill + 1; nudge--; } else{ x = x+ fm.stringWidth(word) + space + toFill; } } } break; } } } class MyMouseAdapter extends MouseAdapter { TextLayout tl; public MyMouseAdapter(TextLayouttl) { this.tl = tl; } public voidmouseClicked(MouseEvent me) { tl.align =(tl.align + 1) % 4; tl.repaint(); } }
让我们来仔细的看一下这个小应用程序是如何工作的。这个小应用程序首先产生一些用来表示对齐方式的常量,然后定义了几个变量。在init()方法中获得了将被显示的文本。
然后它用了一个try-catch块来初始化字体的大小,如果在html中没有写入fontSize参数,那么字体的大小将被设置为14。Text参数是一个很长的用HTML标记
来分段的字符串。
update( )方法在这个例子中是关键。它通过一个font metrics对象设置了字体,获得了基线和字体的高度。接下来,它产生了一个StringTokenizer对象,用它来接受在由text指定的字符串中的下一个分隔符。如果下一个分隔符是
,它将增加竖直空间。否则,update()将检查在当前字体中的分隔符的长度是否超过了列的宽度。如果没有分隔符且行已满,将调用自定义的drawString( )方法来输出此行。
在drawString( )方法中的头三种情况是比较简单的。每一次由line传进来的字符串通过由参数style决定对齐方式为左对齐、右对齐或者居中。当选择LEFTRIGHT方式时,字符串左右两边同时对齐。这意味着我们需要计算剩余的空间(即字符串的宽度和列的宽度之间的差),然后等距离的分布这些字。该类中的最后一个方法在每一次你在小应用程序窗口中点击鼠标时改变参数style。
|
虽然本章覆盖了当你显示文字和图形时将会用到的最重要的属性和最常用的方法,但是它也只是触及到了Java能力的表面。在这个领域,Java将做的越来越好。例如,Java 2为AWT增加了一个被叫做Java 2D的子系统。Java 2D支持更多的关于图形的组件,其中包括像坐标转换、旋转和伸缩等。它也提供了更多的图像特性。如果你对图像处理感兴趣,那么你将可以仔细研究Java 2D。
|
|
1. 看到电视、电脑上的图形显示,特别是一些广告;你能不能想象到这些都是用电脑制作和实现的? 如果要让你去实现,你改怎么构思?
2.或许你用c#语言做过类似QQ一样的软件?那么用java的实现原理和c#比有和异同点呢?
|
|
在本章中,我们主要学习了:
u Java基本图形界面的实现;
u 在面板上,像windows画图工具一样直接作图;
|
|
英文 全文 中文
Component Component 组件
Container Container 容器
Panel Panel 面板
Window Window 窗口
Frame Frame 框架
Canvas Canvas 画布
|
|
你的朋友从事软件开发已经一年多了,和他见面吃饭时,他告诉你,现在的一些商品交易网及股票网上的图形和数据显示是用java的applet实现的,你回来后就想怎么实现,通过本章的学习,你能否实现在网页中显示某些图形呢?(借此模拟将来要做的网页股票显示);