Java事件处理,低级事件类型:键盘事件+焦点事件,你真的懂吗?
低级事件类型
在本节中,将详细讨论与具体用户界面组件无关,但与敲击键盘和活动鼠标有关的事件。下一章将详细讨论有关由用户界面组件产生的语义事件。
键盘事件
当用户按下键盘上的一个键时,就会产生一个ID为KEY_PRESSED的KeyEvent事件。当用户释放一个键时,将会触发ID为KEY_RELEASED的KeyEvent事件。可以使用实现了KeyListener接口的任意类的KeyPressed和KeyReleased方法处理这些事件。这两个方法可以捕获敲击键盘的事件。将这两个方法组合起来就形成了第三个方法KeyTyped,它可以报告由用户敲击键盘所产生的字符。
讲述键盘事件处理过程的最好方法是举例说明。但在此之前,先要介绍几个术语。Java明确区分字符和虚拟键码(virtual key code)。虚拟键码用前缀VK_表示,例如,VK_A或VK_SHIFT。虚拟键码与键盘上的键一一对应。例如,VK_A表示被标记为A的键。虚拟键码没有单独的小写键,即键盘没有单独的小写键。
注意:虚拟键码涉及“扫描码”,这是在按下一个物理键或释放一个物理键时,键盘向计算机发送的编码。
假设用户采用按下SHIFT键的同时按下A键的方式键入大写字母A。为了响应这个用户动作,Java将会产生5个事件,下面是动作和相关的事件:
1)按下SHIFT键(为VK_SHIFT调用keyPressed)。
2)按下A键(为VK_A调用keyPressed)。
3)键入“A”(为“A”调用keyTyped)。
4)释放A键(为VK_A调用keyReleased)。
5)释放SHIFT键(为VK_SHIFT调用keyReleased)。
另一方面,如果用户只是按下A键来键入小写字母“a”,那么只产生3个事件:
1)按下A键(为VK_A调用keyPressed)。
2)键入“a”(为“a”调用keyTyped)。
3)释放A键(为VK_A调用keyReleased)。
因此,keyTyped过程报告键入的字符(“A”或“a”),而keyPressed和keyReleased方法报告用户按下的实际键。
为了能够让keyPressed和keyReleased方法执行,首先需要检查键码(key code)。
键码等于下列(有利于记忆的)常量之一。它们都定义在KeyEvent类中。
为了查明SHIFT、CONTROL、ALT和META键的当前状态,当然可以跟踪VK_SHIFT、VK_CONTROL、VK_ALT和VK_META键是否被按下,但是这样做很麻烦。另外,可以使用sShiftDown、isControlDown、isAltDown和isMetaDown方法。(Sun和Macintosh键盘有一个专门的META键。在Sun键盘上,这个按键标记为菱形。在Macintosh键盘上,这个按键用一个苹果或苜蓿叶标记。)
例如,下列代码检测用户是否按下SHIFT+“→”:
在keyTyped方法中,调用getKeyChar方法得到键入的实际字符。
注意:并不是所有的敲击键盘都会产生ketTyped调用。只有那些产生Unicode字符的敲击才能够在keyTyped方法中捕获。可以使用keyPressed方法检查光标键和其他命令键。
例8-3给出了处理敲击键盘事件的方式。这个程序(如图8-7所示)是Etch-A-Sketch™玩具的简单实现。
用户可以利用光标键,向上、下、左、右移动画笔。如果向下的同时按下SHIFT键,画笔移动的增量比较大。如果你熟悉vi编辑器,就可以使用小写字母h、j、k和l键代替光标键来移动画笔;大写H、J、K和L将更大增量地移动画笔。在这里用keyPressed方法捕获光标键,用keyTyped方法捕获字符。
这里有一个技巧:通常,面板不接受任何键盘事件。因此,可以调用setFocusable方法对默认情形进行覆盖。本章稍后将讨论键盘焦点的概念。
例8-3 Sketch.java
java.awt.event.KeyEvent 1.1
• char getKeyChar( )
返回用户键入的字符。
• int getKeyCode( )
返回该键盘事件的虚拟键码。
• boolean isActionKey( )
如果事件中的键是一个“动作”键,返回true。下面列出的是动作键:HOME、END、PAGE UP、PAGE DOWN、UP、DOWN、LEFT、RIGHT、F1. . .F24、PRINT SCREEN、SCROLL LOCK、CAPS LOCK、NUM LOCK、PAUSE、INSERT、DELETE、ENTER、BACKSPACE、DELETE和TAB。
• static String getKeyText(int keyCode)
返回描述键码的字符串。例如,getKeyText(KeyEvent VK_END)返回字符串"End"。
• static String getKeyModifiersText(int modifiers)
返回描述SHIFT或CTRL+SHIFT这类修饰符键的字符串。
参数:modifiers
修饰符状态,由getModifiers报告
java.awt.event.InputEvent 1.1
• int getModifiers( )
返回一个整型数值,它用位描述SHIFT、CONTROL、ALL和META修饰符的状态。这个方法
既可以应用于键盘事件,也可以应用于鼠标事件。要想检测某一位是否被设置,可以测试返回值
是否对应于位掩码SHIFT_MASK、CTRL_MASK、ALT_MASK、ALT_GRAPH_MASK、
META_MASK或调用下列某一个方法。
• boolean isShiftDown( )
• boolean isControlDown( )
• boolean isAltDown( )
• boolean isAltGraphDown( ) 1.2
• boolean isMetaDown( )
在事件发生时,如果某个修饰符键被按下,方法返回true。
鼠标事件
如果只希望用户能够点击按钮或菜单,就不需要显式地处理鼠标事件。鼠标操作将由用户界面中的各种组件内部处理,并转换成对应的语义事件。然而,如果希望用户使用鼠标画图,就需要捕获鼠标移动点击和拖动事件。
在本节中,将展示一个简单的图形编辑器应用程序,它允许用户在画布上(如图8-8所示)放置、移动和擦除方块。
当用户点击鼠标按钮时,将会调用三个监听器方法:鼠标第一次被按下时调用mousePressed;鼠标被释放时调用mouseReleased;最后调用mouseClicked。如果只对最终的点击事件感兴趣,就可以忽略前两个方法。用MouseEvent类对象作为参数,调用getX和getY方法可以获得鼠标被按下时鼠标指针所在的x和y坐标。要想区分单击、双击和三击(!),需要使用getClickCount方法。
有些用户界面设计者喜欢让用户采用鼠标点击与键盘修饰符组合(例如,CONTROL+SHIFT+CLICK)的方式进行操作。我们感觉这并不是一种值得赞许的方式。如果对此持有不同的观点,可以看看同时检测鼠标按键和键盘修饰符所带来的混乱。在最初的API中,有两个鼠标按钮的掩码与两个键盘修饰符的掩码一样,即
BUTTON2_MASK == ALT_MASK
BUTTON3_MASK == META_MASK
这样做是为了能够让用户使用仅有一个按钮的鼠标通过按下修饰符键来模拟按下其他鼠标键的操作。然而,在JDK 1.4中,建议使用一种不同的方式。有下列掩码:
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK
getModifiersEx方法能够准确地报告鼠标事件的鼠标按钮和键盘修饰符。
需要注意,在Windows环境下,使用BUTTON3_DOWN_MASK检测鼠标右键(非主要的)的状态。例如,可以使用下列代码检测鼠标右键是否被按下:
if ((event.getModifiersEx( )&InputEvent.BUTTON3_DOWN__MASK)! = )
. . . //code for right click
在列举的简单例子中,提供了mousePressed和mouseClicked方法。当鼠标点击在所有小方块的像素之外时,就会绘制一个新的小方块。这个操作是在mousePressed方法中实现的,这样可以让用户的操作立即得到响应,而不必等到释放鼠标按键。如果用户在某个小方块中双击鼠标,就会将它擦除。由于需要知道点击次数,所以这个操作是在mouseClick方法中实现。
当鼠标在窗口上移动时,窗口将会收到一连串的鼠标移动事件。大多数应用程序忽略了这些事件。然而,我们给出的测试程序将捕获这些事件,以便在光标位于不同的小方块之上时变成另外一种形状(十字)。实现这项操作需要使用Cursor类中的getPredefinedCursor方法。表8-2列出了在Windows环境下,鼠标的形状和方法对应的常量。(注意,有若干个光标的形状完全一样,但在其他平台上未必如此。)
提示:可以在jre/lib/images/cursors目录中找到光标图像。在文件cursors.properties中定义了光标“热点”。热点是指示光标引起动作的点。例如,如果光标变成放大镜形状,热点就是该镜的中心。
下面是例子程序中MouseMotionListener类的mouseMoved方法:
注意:可以利用Toolkit类中的createCustomCursor方法自定义光标类型:
createCustomCursor的第一个参数指向光标图像。第二个参数给出了光标的“热点”偏移。第三个参数是一个描述光标的字符串。这个字符串可以用于访问性支持,例如,可以将光标形式读给视力受损或没有在屏幕前面的人。
如果用户在移动鼠标的同时按下鼠标,就会调用mouseMoved而不是调用mouseDragged。在测试应用程序中,用户可以用光标拖动小方块。在程序中,仅仅用拖动的矩形更新当前光标位置。
然后,重新绘制画布,以显示新的鼠标位置。
注意:只有鼠标在一个组件内部停留才会调用mouseMoved方法。然而,即使鼠标拖动到组件外面,mouseDragged方法也会被调用。
还有两个鼠标事件方法:mouseEntered和mouseExited。这两个方法是在鼠标进入或移出组件时被调用。
最后,解释一下如何监听鼠标事件。鼠标点击由mouseClick过程报告,它是MouseListener接口的一部分。由于大部分应用程序仅对鼠标点击感兴趣,而对鼠标移动并不太感兴趣,但鼠标移动事件发生的频率又很高,因此将鼠标移动事件与拖动事件定义在一个称为MouseMotionListener的独立接口中。
在例8-4的程序中,对两种鼠标事件类型都感兴趣。这里定义了两个内部类:MouseHandler和MouseMotionHandler。MouseHandler类扩展于MouseAdapter类,这是因为它只定义了5个MouseListener方法中的两个方法。MouseMotionHandler实现了MouseMotionListener接口,并定义了这个接口中的两个方法。
例8-4是这个程序的清单。
例8-4 MouseTest.java
java.awt.event.MouseEvent 1.1
• int getX( )
• int getY( )
• point getPoint( )
返回事件发生时,事件源组件左上角的坐标x(水平)和y(竖直),或点信息。
• void translatePoint(int x, int y)
通过水平移动x单位,垂直移动y单位来转换事件坐标。
• int getClickCount( )
返回与事件关联的鼠标连击次数。(“连击”所指定的时间间隔与具体系统有关。)
java awt.event.InputEvent 1.1
• int getModifiersEx( ) 1.4
返回事件扩展的或“按下”(down)的修饰符。使用下面的掩码值检测返回值:
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK
• static String getModifiersExText(int modifiers) 1.4
返回用给定标志集描述的扩展或“按下”(down)的修饰符字符串,例如“Shift+Button1”。
java.awt.Toolkit 1.0
• public Cursor createCustomCursor(Image image, Point hotSpot, String name) 1.2
创建一个新的定制光标对象。
参数:image
光标活动时显示的图像
hotSpot
光标热点(箭头的顶点或十字中心)
name
光标的描述,用来支持特殊的访问环境
java.awt.Component 1.0
• public void setCursor(Cursor cursor) 1.1
用光标图像设置给定光标。
焦点事件
用鼠标可以指向屏幕上的任何一个对象。但是在使用键盘输入时,敲击键盘必须定位于一个特定的屏幕对象。窗口管理器(window manager,例如,Windows或X Windows)直接将所有的击键定位于活动窗口(active window)。通常,活动窗口用高亮度显示的标题栏进行区分。在任何时刻,只有一个窗口可以是活动的。
现在假设活动窗口在一个Java程序控制之下。当Java窗口接收到敲击键盘的操作,并定位于某个特定的组件时,这个组件就具有了焦点(focus)。正像活动窗口可以采用某种方式进行辨别一样,大多数的Swing组件在具有焦点的时候,也可以拥有一个明显的提示。文本域会显示闪烁的光标;按钮上的标签周围有一个矩形等等。当文本域具有焦点的时候,可以将文本输入到文本域中;当按钮有焦点的时候,可以通过敲击空格键来“点击”这个按钮。
在一个窗口中,最多只有一个组件拥有焦点。如果用户点击另一个组件,那么刚才拥有焦点的组件就会失去(lose)焦点,而被点击的组件就会获得焦点。用户还可以使用TAB键在各个组件上轮流切换焦点。这样可以遍历到所有能够接收输入焦点的组件。在默认情况下,Swing组件按照放置在容器中的位置,从上到下,从左到右的顺序遍历。也可以改变焦点遍历的顺序,有关这个主题更加详细的内容请参阅下一章。
幸运的是,大多数应用程序程序员不必过分地为焦点处理而担忧。在JDK 1.4之前,对组件焦点事件的捕获主要用于核查错误与校验数据。假设在一个文本域中包含信用卡号。当用户编辑完毕这个域,并将焦点移至另外一个域上时,就应该捕获失去焦点事件。如果信用卡号的格式不正确,就立即显示一个错误消息,并将焦点返回到信用卡域上。然而,JDK 1.4有一个更加健壮、更加简易的数据校验机制。有关这部分内容将在第9章中讨论。
有些组件,例如,标签和面板,在默认情况下无法得到焦点,这是因为它们主要用于装饰或分组。如果希望编写一个程序,能够根据用户的击键操作在面板上绘制图形,就要对默认处理进行覆盖。在JDK 1.4中,可以通过简单地调用下面这个方法达到目的。
panel.setFocusable(true);
注意:在旧版本的JDK中,要想达到同样的效果,必须覆盖组件的isFocusTraversable方法。
然而,在旧版本的焦点实现中,获得焦点与遍历焦点在概念上是分开的。这种分离导致行为上的混淆,现在已经被废除。isFocusTraversable方法已经不再提倡使用了。
在本节的剩余部分,将讨论焦点事件的详细内容,这些内容完全可以等到需要精心设计焦点控制时再阅读。
在JDK 1.4中,可以很容易地发现:
• 焦点拥有者,也就是拥有焦点的那个组件。
• 焦点窗口,也就是包含焦点拥有者的窗口。
• 活动窗口,也就是包含焦点拥有者的框架或对话框。
焦点窗口通常与活动窗口是一样的。只有在焦点拥有者包含在没有框架修饰的顶层窗口(例如,弹出式菜单)中时才有所差别。
为了得到这些信息,首先要获得键盘焦点管理器:
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager( );
然后,调用:
Component owner = manager.getFocusOwner( );
Window focused = manager.getFocusedWindow( );
Window active = manager.getActiveWindow( ); //a frame or dialog
为了能够得到焦点变化的通知,需要将焦点监听器安装到组件或窗口中。一个组件焦点监听器必须实现FocusListener接口及两个方法focusGained和focusLost。当组件获得或失去焦点时将会触发这两个方法。这两个方法都有一个FocusEvent类的参数。在这个类中有几个很有用的方法。getComponent方法负责报告获得或失去焦点的组件;isTemporary方法将在焦点发生临时性地改变时返回true。临时性的焦点改变是指组件临时性地失去控制,但又可以自动地找回焦点。例如,当用户选择不同的活动窗口时就会发生这种情况。只要用户再次选择当前的窗口,同一个组件就会重新获得焦点。
JDK 1.4引入了窗口焦点事件的传递,为此,需要将WindowFocusListener添加到窗口,并实现windowGainedFocus和windowLostFocus方法。
在JDK 1.4中,当焦点转移的时候,可以找到“对等物”组件或窗口。对等物是指在组件或窗口失去焦点时获得焦点的组件或窗口。相反地,当组件或窗口获得焦点时,对等物是刚刚失去焦点的那个组件或窗口。FocusEvent类中的getOppositeComponent方法将报告对等组件,WindowEvent类中的getOppositeWindow方法将报告对等的窗口。
可以通过调用Component类中的requestFocus方法,利用程序将焦点从一个组件移到另一个组件上。然而,如果组件没有包含在当前的焦点窗口中,其行为将与平台有关。为了能够让程序员开发与平台无关的代码,JDK 1.4在Component类中增加了一个方法requestFocusInWindow。这个方法只有在组件包含在焦点窗口内时才会成功。
注意:不应该在requestFocus或requestFocusInWindow返回true时就认定组件拥有焦点,而应该等待FOCUS_GAINED事件的发送。
注意:有些程序员觉得FOCUS_LOST事件有些混乱,并试图在focusLost处理器中通过请求获得焦点来阻止其他组件。然而,在这个时候,焦点已经失去了。如果必须在一个特定的组件中捕获焦点,就需要在KeyboardFocusManager中安装“禁止改变监听器”,并禁用focusOwner属性。有关禁用属性的详细介绍请参阅卷II第8章。
java.awt.Component 1.0
• void requestFocus( )
请求组件获得焦点。
• boolean requestFocusInWindow( ) 1.4
请求组件获得焦点。如果该组件没有包含在焦点窗口内,返回true,或者请求由于其他原因请求没有实现,返回flase。
• void setFocusable(boolean b) 1.4
• boolean isFocusable( ) 1.4
设置或获取该组件的“focusable”状态。如果b为true,则该组件可以获得焦点。
• boolean isFocusOwner( ) 1.4
如果该组件当前拥有焦点,返回true。
java.awt.KeyboardFocusManager 1.4
• static KeyboardFocusManager getCurrentKeyboardFocusManager( )
获得当前焦点管理器。
• Component getFocusOwner( )
得到拥有焦点的组件,如果焦点管理器没有管理拥有焦点的组件,返回null。
• Window getFocusedWindow( )
得到包含拥有焦点组件的窗口,如果焦点管理器没有管理拥有焦点的组件,返回null。
• Window getActiveWindow( )
得到对话框或包含焦点窗口的框架,如果焦点管理器没有管理焦点窗口,返回null。
java.awt.Window( ) 1.0
• boolean isFocused( ) 1.4
如果该窗口是当前的焦点窗口,返回true。
• boolean isActive( ) 1.4
如果该框架或对话框是当前的活动窗口,返回true。活动框架和对话框的标题栏通常由窗口管理器标识出来。
java.awt.event.FocusEvent 1.1
• Component getOppositeComponent( ) 1.4
返回在focusGained处理器中失去焦点的组件,或者在focusLost处理器中获得焦点的组件。
java.awt.event.WindowEvent 1.4
• Window getOppositeWindow( ) 1.4
返回在windowGainedFocus处理器中丢失焦点的窗口,在windowLostFocus处理器中获得焦点的窗口,在windowActivated处理器中变为不活动的窗口,或者在windowDeactivated处理器中被激活的窗口。
java.awt.event.WindowFocusListener 1.4
• void windowGainedFocus(WindowEvent event)
当事件源窗口获得焦点时调用这个方法。
• void windowLostFocus(WindowEvent event)
当事件源窗口失去焦点时调用这个方法。
觉得文章不错的话,可以转发关注一下小编,小编每天都会持续更新的!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。