Java Swing Key Binding Example
Places where KeyListener
is used in order to map key input to some action, it is more convenient and preferred from usimg Keymap
or InputMap
and ActionMap
combination instead. Using Keymap
or InputMap
and ActionMap
combination, it is easier to map the action to a specific key stroke and thus key binding is achieved.
1. Introduction
Most of the manipulations performed on text components involve keyboard operations. Key binding helps us to map keyboard to action mapping.
- InputMap and ActionMap:
InputMap
andActionMap
are class members ofjavax.swing
. For each JComponent control we can extract the correspondingInputMap
andActionMap
.InputMap
andActionMap
both are just tables or maps where first one binds key strokes by means ofKeyStroke
objects to action names and second one specifies actions corresponding to each action name. EachInputMap
/ActionMap
has a parent that typically comes from UI. Anytime the look and feel is changed, the parent is reset. Thus any binding specified by the developer is maintained across different look and feel. EachJComponent
has 3InputMap
s and 1ActionMap
.InputMap
s correspond toJComponent.WHEN_FOCUSED
,JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
andJComponent.WHEN_IN_FOCUSED_WINDOW
. - Keymap:
Keymap
actually helps to map keyboard events to actions. It is not absolutely necessary that text operation should useKeymap
but text operations not usingKeymap
do not perform well Swing’s pluggable look-and-feel environment.
Keymap
is an interface defined injavax.swing.text
package. TheJTextComponent
class has a defaultKeymap
implementation as well as some static methods for manipulating Keymap.
AKeymap
is map of key event to some kind of command that gets executed whenever the said event is triggered. In an application, if it is no further customized, we can have sharedKeymap
created for each ofJTextField
,JPasswordField
,JTextArea
,JTextPane
andJEditorPane
. Whatever the instances of any of the type of component can be created, those will share the sameKeymap
instance. Whichever component will have focus, action will take effect on that only. - KeyStroke:
InputMap
andKeymap
maps oneKeyStroke
object to a action name or action.KeyStroke
class is a member ofjavax.swing
package and any object of this class represents a key or a combination of keys in the keyboard.
2. Technologies Used
- Java (jdk 1.6.x or higher will be fine)
- Eclipse ( Galileo or higher version is required)
3. API Description
KeyStroke
:
KeyStroke
object is retrieved by using API call like KeyStroke key = KeyStroke.getKetStroke(...)
Method Signature | Explanation | Example |
getKeyStroke(char keyChar) | Returns a shared instance of a KeyStroke that represents a KEY_TYPED event for the specified character. | getKeyStroke(‘Z’);
|
getKeyStroke(Character keyChar, int modifiers) | Returns a shared instance of a KeyStroke that represents a KEY_TYPED event for the specified Character object and a set of modifiers. | getKeyStroke(new Character(‘Z’), InputEvent.SHIFT_MASK) |
getKeyStroke(String s) | Parses a string and returns a KeyStroke. | getKeyStroke(“control alt 7”) |
getKeyStroke(int keyCode, int modifiers) | Returns a shared instance of a KeyStroke, given a numeric key code and a set of modifiers. | getKeyStroke(KeyEvent.VK_F4, InputEvent.SHIFT_MASK) |
getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease) | Returns a shared instance of a KeyStroke, given a numeric key code and a set of modifiers, specifying whether the key is activated when it is pressed or released. | getKeyStroke(KeyEvent.VK_ENTER, 0, true) |
getKeyStrokeForEvent(KeyEvent anEvent) | Returns a KeyStroke which represents the stroke which generated a given KeyEvent. |
InputMap
andActionMap
:
The InputMap
is defined as any of the 4 alternatives mentioned below.
ComponentObject.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(...),"Action Name")
ComponentObject.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(...),"Action Name")
ComponentObject.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(...),"Action Name")
ComponentObject.getInputMap().put(KeyStroke.getKeyStroke(...),"Action Name")
. (Option 1 and Option 4 are equivalent statements given thatKeystroke
objects are same.
The ActionMap
can be defined as described below
ComponentObject.getActionMap().put("Action Name","Action Object")
. ‘Action Object’ is instance of a class extended from javax.swing.AbstractAction
class. As part of the implementation, overridden actionPerformed
method takes care of the action to be performed.
Keymap
:
Keymap
object of a JComponent type component can be found as ComponentObject.getKeymap()
. Commonly used API details are mentioned as below.
Method Signature | Description |
void addActionForKeyStroke(KeyStroke key, Action a)
| Adds a binding to the keymap. |
void removeKeyStrokeBinding(KeyStroke keys)
| Removes a binding from the keymap. |
KeyStroke[] getKeyStrokesForAction(Action a)
| Fetches the keystrokes that will result in the given action. |
4. Description of Key Binding features in the example
Keymap
action is defined for ‘Shift F1’ key combination on JTextField
components used in the example for entering ‘First Name’, ‘Middle Name’ and ‘Last Name’. If ‘Shift F1’ key combination is used keeping focus on any of these three components, content of the component appears in another non editable JTextField
component i.e. Full Name.
Keeping focus in ‘Middle Name’ field, if Shift+F1 is pressed, since action corresponding to Shift+F1 key combination is shared by all those JTextField
components, the content of ‘Middle Name’ field is added in ‘Full Name’ field.
Keeping focus in ‘Last Name’ field, if Shift+F1 is pressed, since action corresponding to Shift+F1 key combination is shared by all those JTextField
components, the content of ‘Last Name’ field is added in ‘Full Name’ field.
Keymap
action is defined for ‘Shift Alt F1’ key combination on JTextField
components used in the example for ‘Full Name’ and for entering ‘Address1’, ‘Address2’ , ‘Pin’, ‘Cell No’ and ‘Email Id’. If ‘Alt Shift F1’ key combination is used keeping focus on any of these components, content of the component appears in another non editable JTextArea
component on the left pane. In the below mentioned screenshot the impact is shown for ‘Full Name’ text field.
Keeping focus in ‘Address 1’ field, if Alt+Shift+F1 is pressed, since action corresponding to Alt+Shift+F1 key combination is shared by all those JTextField
components, the content of ‘Address 1’ field is added in JTextArea
field on the left pane.
Keeping focus in ‘Address 2’ field, if Alt+Shift+F1 is pressed, since action corresponding to Alt+Shift+F1 key combination is shared by all those JTextField
components, the content of ‘Address 2’ field is added in JTextArea
field on the left pane.
5. Description of key binding features in the source code
SwingKeyMapExampleFrame.java
package com.javacodegeeks.example.swing.keymap; import java.awt.Button; import java.awt.CardLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; import javax.swing.text.TextAction; public class SwingKeyMapExampleFrame extends JFrame { /** * */ private static final long serialVersionUID = -1927825705131809212L; private JTextField firstName = new JTextField(15); private JTextField middleName = new JTextField(15); private JTextField lastName = new JTextField(15); private JTextField fullName = new JTextField(60); private JTextField address1 = new JTextField(50); private JTextField address2 = new JTextField(20); private JTextField pin = new JTextField(6); private JTextField phoneNo = new JTextField(10); private JTextField emailId = new JTextField(30); private JTextArea textArea = new JTextArea(40, 50); private JScrollPane scrollText = new JScrollPane(textArea); public SwingKeyMapExampleFrame(){ setSize(500,600); setTitle("JTextField Demo"); JSplitPane splitPane = new JSplitPane(); splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT); splitPane.setDividerLocation(250); textArea.setEditable(false); splitPane.setLeftComponent(scrollText); add(splitPane); JPanel panel = new JPanel(new GridLayout(0,1)); splitPane.setRightComponent(panel); JPanel namePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel fullNamePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel addressPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel contactsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel submitPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); addNameControls(namePanel,fullNamePanel); addAddressControls(addressPanel); addContactsControls(contactsPanel); panel.add(namePanel); panel.add(fullNamePanel); panel.add(addressPanel); panel.add(contactsPanel); panel.add(submitPanel); } private void addNameControls(JPanel namePanel, JPanel fullNamePanel){ JLabel fName = new JLabel("First name: "); namePanel.add(fName); firstName.setBackground(Color.YELLOW); firstName.setName("First Name"); Keymap firstNameMap = firstName.getKeymap(); KeyStroke altF1 = KeyStroke.getKeyStroke(KeyEvent.VK_F1,InputEvent.SHIFT_MASK); firstNameMap.addActionForKeyStroke(altF1, new TextFieldAction(fullName)); namePanel.add(firstName); JLabel mName = new JLabel("Middle name: "); namePanel.add(mName); middleName.setBackground(Color.YELLOW); middleName.setName("Middle Name"); namePanel.add(middleName); JLabel lName = new JLabel("Last name: "); namePanel.add(lName); lastName.setBackground(Color.YELLOW); lastName.setName("Last Name"); namePanel.add(lastName); addFullNameControls(fullNamePanel); } private void addFullNameControls(JPanel fullNamePanel){ JLabel fullNameTxt = new JLabel("Full name: "); fullNamePanel.add(fullNameTxt); fullName.setEditable(false); fullName.setName("Full Name"); Keymap fullNameMap = fullName.getKeymap(); KeyStroke altF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F1,InputEvent.SHIFT_MASK|InputEvent.ALT_MASK); fullNameMap.addActionForKeyStroke(altF3, new ScrollTextFieldAction(textArea)); fullNamePanel.add(fullName); } private void addAddressControls(JPanel addressPanel){ JLabel address1Lbl = new JLabel("Address1: "); addressPanel.add(address1Lbl); address1.setName("Address1"); addressPanel.add(address1); JLabel addressLb2 = new JLabel("Address 2: "); addressPanel.add(addressLb2); address2.setName("Address2"); addressPanel.add(address2); JLabel addressLb3 = new JLabel("Pin: "); addressPanel.add(addressLb3); pin.setName("Pin"); addressPanel.add(pin); } private void addContactsControls(JPanel contactPanel){ JLabel phone = new JLabel("Cell No: "); contactPanel.add(phone); phoneNo.setName("Phone No"); contactPanel.add(phoneNo); JLabel email = new JLabel("Email Id: "); contactPanel.add(email); emailId.setName("Email Id"); contactPanel.add(emailId); } }
- line 93 – 96:
Keymap
object is retrieved fromfirstName
object of theJTextField
component. Without customization, allJTextField
objects shareKeymap
object instance. This will also be shared among all those existing. In theKeymap
, corresponding toKeyStroke
object for key combination Shift+F1, one customizedAction
object is mapped. - line 123 – 125:
Keymap
object is retrieved fromfullName
object of theJTextField
component. Without customization, allJTextField
objects shareKeymap
object instance. This will also be shared among all those existing. In theKeymap
, corresponding toKeyStroke
object for key combination Alt+Shift+F1, one customizedAction
object is mapped.
TextFieldAction.java
/** * */ package com.javacodegeeks.example.swing.keymap; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import javax.swing.JTextField; import javax.swing.text.TextAction; /** * @author ab * */ public class TextFieldAction extends TextAction { private JTextField fullName; public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub String text = getTextComponent(arg0).getText(); if("First Name".equalsIgnoreCase(getTextComponent(arg0).getName())){ if(text != null && text.length()> 0) if(fullName.getText() == null || fullName.getText().length() == 0){ fullName.setText(text); }else{ fullName.setText(text+" "+fullName.getText()); } } else if("Middle Name".equalsIgnoreCase(getTextComponent(arg0).getName())){ if(text != null && text.length()> 0) if(fullName.getText() == null || fullName.getText().length() == 0){ fullName.setText(text); }else if(fullName.getText().indexOf(" ") == -1){ fullName.setText(fullName.getText()+" "+text); }else{ String currentContent = fullName.getText(); currentContent = currentContent.substring(0,currentContent.indexOf(" "))+" "+text+currentContent.substring(currentContent.lastIndexOf(" ")); fullName.setText(currentContent); } }else{ if(text != null && text.length()> 0) if(fullName.getText() == null || fullName.getText().length() == 0){ fullName.setText(text); }else{ String currentContent = fullName.getText(); fullName.setText(currentContent+" "+text); } } } /** * @param arg0 */ public TextFieldAction(JTextField fullName) { super("Convert to upper case"); // TODO Auto-generated constructor stub this.fullName = fullName; } }
- line 20 – 51: Overridden implementation of
actionPerformed
method of the Action class extended fromTextAction
class. This implementation gives the effect of Shift+F1 key press on the saidJTextField
components.
6. Summary
In this example key binding is exhibited by means of using Keymap
. For more generalized purpose we can use InputMap
and ActionMap
combination or even combination of Keymap
and InputMap
– ActionMap
and associated rich APIs can be used appropriately. This approach is very useful in cases like Java based game development etc. where keyboard is extensively used and proper key to action mapping is something which is essential part of it. For further reading, links shared in this post can be referred.
7. Download the Source Code
This was an example of Java Key Binding
.
You can download the full source code of this example here: KeyBindingExample