在Java Swing程式設計過程中,經常需要處理鍵盤事件,例如處理快捷鍵等。這裡就介紹如何定義鍵盤事件,以及如何處理這些事件。
在jdk1.2中,分别針對Jcomponent和Text類的對象定制了不同的處理鍵盤事件的方法:在Jcomponent中,定義了registerKeyboardAction方法,使用這個方法來将需要處理的鍵盤事件以及處理事件的行為綁定在一起。Text類中具有keymap對象,同Jcomponent中的處理方法類似,這個對象儲存着需要處理的鍵盤事件和對應的行為。
而在jdk1.3中,使用一種新的方法來處理鍵盤事件,它将jdk1.2的兩種方法整合在一起。不需要區分被處理的是Jcomponent還是Text類型的元件。它定義了兩個新的類:InputMap和ActionMap。他們均是簡單的表或映射。一個InputMap将一個Keystroke對應到一個對象,ActionMap将一個對象對應到一個行為(Action)。通常InputMap中KeyStroke所對應的對象是一個字元串,通過這個字元串可以在ActionMap中查找到相應的行為。
InputMap和ActionMap中均有put方法。InputMap的put方法可以将Keystroke對應到一個對象,而ActionMap的put方法可以将一個對象對應到一個行為。
在每一個Jcomponent元件中,會有三個預設的InputMap和一個預設的ActionMap。他們可以通過調用getInputMap(int condition)和getActionMap()得到。三個InputMap分别是當元件本身擁有焦點時的InputMap(WHEN_FOCUSED),當元件的祖先擁有焦點時的InputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)群組件所在的窗體具有焦點時的InputMap(WHEN_IN_FOCUSED_WINDOW)(括号内表示為了得到這些InputMap,應該在getInputMap中設定的參數)。以下分别說明這三種InputMap:
1. 元件本身擁有焦點時的InputMap:當元件擁有焦點時,鍵盤按鍵按下,則java在這個InputMap中查找鍵盤事件所對應的KeyStroke對象。
2. 元件的祖先擁有焦點時的InputMap:當元件的祖先擁有焦點時,鍵盤按鍵按下,則java查找這個InputMap。
3. 元件所在的視窗擁有焦點時的InputMap:當元件所在的視窗具有焦點時,鍵盤按鍵按下,則java查找這個InputMap。
當一個鍵被按下,這個事件被轉化成一個KeyStroke對象,java會查找這個Jcomponent的相應InputMap(例如,當元件的祖先具有焦點時,java就查找這個Jcomponent的祖先擁有焦點的InputMap)中是否有這個KeyStroke,如果有,取出它所對應的對象(通常是字元串),利用這個對象在這個Jcomponent的ActionMap中查找,如果找到對應的行為(Action),則java執行這個行為的actionPerformed方法(随後介紹這個方法)。進而達到處理鍵盤事件的目的。
每一個InputMap可以具有parent屬性,這個屬性的值是一個InputMap。當在一個InputMap中查找不到鍵盤事件的KeyStroke時,java會自動在它的parent屬性指定的InputMap中查找,依次向上查找,直至找到。使用parent的好處是:當有一些固定的,不希望使用者進行改動的鍵盤映射可以存放在parent屬性所指定的InputMap中,進而避免被意外修改;另外可以将多個Jcomponent的預設InputMap設定具有相同的parent,使得可以共享一些鍵盤綁定的設定。可以通過InputMap類的setparent()方法設定它的parent屬性。ActionMap也具有相同的parent屬性,使用方法也相同。
以上是如何将一個鍵盤事件對應到一個行為,以下就簡單介紹行為(Action)。
行為是一個實作了Action接口的類。在Action接口中定義了7個方法。其中最關鍵的是actionPerformed()方法。這個方法描述了這個行為的具體操作過程。其他幾個方法包括setEnabled,isEnabled,putValue,getValue,addPropertyChangeListener,和removePropertyChangeListener方法。他們分别用來設定行為是否可用、判斷行為可用的狀态、設定和取得行為的一些屬性,最後兩個方法用來允許其他對象在行動對象的屬性發生變化後得到通知。
通常我們使用一個實作了Action接口的大部分方法的抽象類AbstractAction類作為基類,重載actionPerformed方法以實作我們的行為。
我們用一個例子來具體說明如何進行實際的操作。
首先編寫一個具體的行為,對指定的鍵盤事件進行處理:
建立四個TextAction對象:
TextAction whenFocusSon = new TextAction("focus son");
TextAction whenFocusFather = new TextAction("focus father");
TextAction window = new TextAction("window");
TextAction ancestor = new TextAction("ancestor");
随後,在一個窗體中加入兩個面闆,名為sonPanel和parentPanel,使得parentPanel是sonPanel的祖先。并在sonPanel中加入一個名為son的button,在parentPanel中加入名為parent的button。在fatherPanel外加入幾個button。
得到son元件的三個InputMap,并建立一個名為focusFatherIm的InputMap,使得這個InputMap成為focusIm的parent:
運作程式及其相應結果:
1. 單擊son按鈕,這時如果按下'f','F','a','w',程式均會有相應的輸出。這是因為,此時的焦點在son按鈕上,而son按鈕元件的三個InputMap都是有效的。是以他們對應的事件都會發生。
2. 單擊parent按鈕,這時按下'w',程式會有相應的輸出。而按下'f','F','a',程式沒有反應。這是因為parent按鈕具有焦點,這個按鈕不是son按鈕的祖先,而son所在的視窗具有焦點,是以隻有元件所在視窗具有焦點的InputMap是有效的。
3. 單擊其他的按鈕(parentPanel外的按鈕),這時按下'w',程式會有相應的輸出。而按下'f','F','a',程式沒有反應。這是因為這些按鈕具有焦點,他們不是son按鈕的祖先,而son所在的視窗具有焦點,是以隻有元件所在視窗具有焦點的InputMap是有效的。
附:主要程式代碼: