第五章 输入、界面和网络

作者:刘啸林
您可以自由下载本文,但请保留作者信息。


     在上一章中,介绍了如何在Java的Applet中绘制图形、动画和加入声音。这一章要介

绍如何设计人机交互界面、如何接收用户的输入以及如何处理这些输入。

5.1 接收鼠标、键盘的输入

     Java中的事件(Event)处理是AWT(Abstract WindowingToolkit)的一部分。当某些情?

况发生时,例如鼠标的移动、键盘的按键等,会触发相应的事件。而通过这些事件,AWT

构件与用户或AWT构件之间就可以进行某种通讯。AWT对事件的处理分为两种情况,一种

是由AWT或浏览器负责处理,例如paint()方法,还有一种是不作处理,例如鼠标的移动。

在这一节里,我们要讨论的是对于这些AWT不作处理的方法,如何自己编写并覆盖这些事

件处理方法,例如接收鼠标、键盘的输入。关于AWT如何处理这些事件,在本节的最后一

部分将专门讨论。为了接收鼠标、键盘的输入,可把它们相应的事件分成三类:鼠标的按

键;鼠标的移动或拖动;键盘的输入。

5.1.1 鼠标的按键

     对于鼠标的按键,AWT会产生两种事件。当鼠标左键按下时产生mouseDown事件,当

鼠标左键弹起时产生mouseUp事件。对于这两种事件,系统会分别调用相应的处理方法

mouseDown()和mouseUp()。所以为了在程序中接收这两种事件,需要在程序中覆盖这两个

事件的处理方法。例如对mouseDown()方法,其调用方式为:

public boolean mouseDown(Event evt, int x, int y) {
………
}
     其中X,Y是事件发生时鼠标的位置,evt参数是由系统产生的Event类的一个实例,

它包含了关于这个事件的一些信息。如果将程序改写为:

public boolean mouseDown(Event evt, int x, int y) {
		System.out.println(*A mouse click happened*);
		return true;
}
    那么在每次鼠标按下时,都会输出这句话。mouseUp()的使用与mouseDown()是相同的。

有一点需要说明的是,这个方法必须返回一个布尔值。返回什么样的布尔值,取决于方法

对事件的处理。如果返回true,则表明方法已完成对事件的处理;如果返回false,则表

明需要其它AWT构件来处理。在大多数情况下,都是返回true。

下面是使用mouseDown()方法的一个较完整的例子。

1:	import java.awt.Graphics;
2:	import java.awt.Color;
3:	import java.awt.Event;
4:
5:	public class triangle extends java.applet.Applet {
6:	    int xspots = new int[3];
7:	    int yspots = new int[3];
8:	    int currspots = 0;
9:
10:	    public void init() {
11:	        setBackground(Color.white);
12:	    }
13:	
14:	    public boolean mouseDown(Event evt, int x, int y) {
15:	        if (currspots <3) { 16: xspots[currspots]="x;" 17: yspots[currspots]="y;" 18: currspots ++; 19: repaint(); 20: } 21: return true; 22: } 23: 24: public void paint(Graphics g) { 25: int i; 26: g.setColor (Color.green); 27: for (i="0;i" < 3; i++) { 28: g.fillOval(xspots[i] 2,yspots[i] 2,4,4); 29: } 30: if (currspots="=" 3) { 31: g.setColor(Color.red); 32: for (i="1;" i<3; i++) { 33: g.drawLine(xspots[i-1],yspots[i-1],xspots[i],yspots[i]); 34: } 35: g.drawLine(xspots[i-1], yspots[i-1], xspots[0], yspots[0]); 36: } 37: } 38: } 这个程序的功能是用鼠标在屏幕上选定三个点,然后将这三个点连成一个三角形。 现在我们来解释一下这个程序。这个程序中使用了三个AWT类:Graphics,Color和Event 类,这在程序的1-3行已标出。也可只用一句 import java.awt.*; 6-8行中,程序中使用了两个数组xspots和yspots来储存三个点的坐标值(在paint()方 法中需要使用),另外设置一个变量currspots来记录已画点的个数。 14-22行是mouseDown方法。每次鼠标按下后,都调用方法mouseDown()。它首先判断是否 已画满三个点。如果未满,则在数组中加入该点的坐标值,并调用repaint()方法;如果 已满,则不做处理。 24-37行是paint方法。在上章中讲到,repaint()方法的功能是刷新背景,并调用paint() 方法。因此在paint()方法中,首先将已有的点重新标出(原有点已被刷新),然后判断 如果已有三个点,那么将三个点连成一个三角形。 

5.1.2 鼠标的移动

   鼠标的移动有两种情况,鼠标的移动和鼠标的拖动,同样也对应了两个方法mouseDrag

和mouseMove。其调用方式和使用方法与mouseDown等相同:

public boolean mouseDrag( Event evt, int x, int y) {
………
}
下面是一个使用mouseDrag方法的例子。
1:	import java.awt.*;
2:
3:	public class drawcircle extends java.applet.Applet {
4:	 	Point start,end;
5:
6:	    public void init() {
7:	        setBackground(Color.white);
8:	    }
9:
10:	    public boolean mouseDown(Event evt, int x, int y) {
11:	        start = new Point(x,y);
12:	        return true;
13:	    }
14:
15:	    public boolean mouseDrag(Event evt, int x, int y) {
16:	        end = new Point(x,y);
17:	        repaint();
18:	        return true;
19:	    }
20:
21:	    public void paint(Graphics g) {
22:	       g.setColor(Color.blue);
23:	       if (start.x <= end.x) 24: if (start.y <="end.y)" 25: g.drawOval(start.x, start.y, end.x-start.x, end.y-start.y); 26: else g.drawOval(start.x, end.y, end.x-start.x, start.y-end.y); 27: else 28: if (start.y <="end.y)" 29: g.drawOval(end.x, start.y, start.x-end.x, end.y-start.y); 30: else g.drawOval(end.x, end.y, start.x-end.x, start.y-end.y); 31: } 32: } 这个程序是用来模拟Windows中Paintbrush的画圆处理。它的处理方法是鼠标按下的 位置是起始点,然后拖动鼠标,松开鼠标时的位置为结束点,在这两点形成的长方形中画 一个内切椭圆。这个程序中第4行,使用了两个Point类型的变量start和end来存放起始点 和结束点的坐标。 10-13行是mouseDown方法,它的功能是在每次鼠标按下时给起始点赋值, 15-19行是mouseDrag方法。因为在鼠标拖动时,结束点的位置尚未确定,所以,在每次 调用mouseDrag时,都要设置新的结束点坐标,并调用repaint方法。一但鼠标松开,就以 最后一次调用mouseDrag时确定的坐标为结束点坐标。 21-31行是paint方法,这里考虑到起始点与结束点的位置关系,所以用了双重判断,并 调用画圆方法。这个程序执行的结果是只能在屏幕上画出一个圆,这是因为程序中只有一 对变量来储存起始点和结束点的坐标。每次画新圆时,已画的圆就被刷新。如果象上小节 的例子中,设置两个数组,则可以把画出的圆保留下来。但这个方法也有缺点,能显示的 圆的数量受数组大小的限制。最好的解决办法是用上章中提到的双重缓冲的思想,使用一 个缓冲区来保存屏幕上的图像。 除了mouseDrag和mouseMove以外,Java中还提供两个相似的方法mouseEnter和 mouseExit。这两个方法是在鼠标进入和离开一个Applet画面时被调用。它们的调用方式 与前面的几个相同。 public boolean mouseEnter (Event evt, int x, int y) { ……… } 

5.1.3 键盘的输入

     当用户按下或松开键盘的按键时,会产生键盘事件。相应的处理方法是keyDown和

keyUp。它们的调用方式与上面的略有不同。

public boolean keyDown (Event evt, int key) {
···
}

     这里的key参数是一个整数变量,是所按下的键的ASCII值。可以用类型转换

(char)key将其转成字符类型。keyUp的调用方式相同。这两种方法的区别是使用场合不

同。如果将按住键不放解释为一连串相同的输入时,用keyDown来处理;而将按住键不放

解释为一个字符输入时,用keyUp来处理。

     下一个问题是对于某些特殊键,如Home、End、PageUp、PageDown和方向键等,如何

处理。最容易想到的是根据ASCII值来判断,但有谁记得住它们的ASCII值呢?Java中提供

了较为简单的方法,它将这些特殊ASCII值定义成Event类的变量。例如判断是否Home键,

可以写成:

if (key = = Event.HOME) {
    ………
}

     这样在编写程序时,就方便多了。表一是这些特殊键所对应的常量。

后面三个键是组合键,Shift,Ctrl,Alt键,这些键与其它键一起使用往往有特殊的含

义。在Java的Event类还另外提供三个方法shiftDown(),metaDown(),controlDown()来

判断Shift、Alt、Ctrl键是否按下。使用方法如下:

public boolean keyDown (Event evt, int key) {
		if (evt.shiftDown())
			………    //显示大写字母
		else
			………    //显示小写字母
}

5.1.4 事件处理器(Event Handler)

     在Java的AWT中有一个事件处理器(Event Handler),事件处理器负责接收发生的事

件,并对事件作出处理。上面介绍的这些常用的事件处理方法实际上都是由事件处理器来

调用的。事件处理器的调用方式是

public boolean handleEvent(Event evt) {
···
}
     在事件处理器中根据Event类参数evt的id值来判断发生事件的种类,针对用户界面

(UI)构件的事件(下一节将会介绍)、键盘的按键、鼠标的操作、窗口的创建、移动、撤

销等等。如果手头有Java API reference的话,可以自己查阅一下。

     在某中情况下如果需要覆盖handleEvent方法,应相当慎重。因为对于原来的这些事

件处理方法,除非在覆盖后的方法中加以指出,否则是不会被调用的。比较可靠的覆盖方法是:

public boolean handleEvent (Event evt) {
		if (evt.id == Event.KEY_PRESS ) {
			………    //处理键盘操作
			return true;
		}
		else {
			return super.handleEvent(evt);
		}
}
这里是将余下的事件处理交给handleEvent的父类处理,从而保证对事件的正常处理。