swing

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 and ActionMap are class members of javax.swing. For each JComponent control we can extract the corresponding InputMap and ActionMap. InputMap and ActionMap both are just tables or maps where first one binds key strokes by means of KeyStroke objects to action names and second one specifies actions corresponding to each action name. Each InputMap/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. Each JComponent has 3 InputMaps and 1 ActionMap. InputMaps correspond to JComponent.WHEN_FOCUSED, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and JComponent.WHEN_IN_FOCUSED_WINDOW.
  • Keymap: Keymap actually helps to map keyboard events to actions. It is not absolutely necessary that text operation should use Keymap but text operations not using Keymap do not perform well Swing’s pluggable look-and-feel environment.
    Keymap is an interface defined in javax.swing.text package. The JTextComponent class has a default Keymap implementation as well as some static methods for manipulating Keymap.
    Keymap 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 shared Keymap created for each of JTextField, JPasswordField, JTextArea, JTextPane and JEditorPane. Whatever the instances of any of the type of component can be created, those will share the same Keymap instance. Whichever component will have focus, action will take effect on that only.
  • KeyStroke: InputMap and Keymap maps one KeyStroke object to a action name or action. KeyStroke class is a member of javax.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 and ActionMap:

The InputMap is defined as any of the 4 alternatives mentioned below.

    1. ComponentObject.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(...),"Action Name")
    2. ComponentObject.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(...),"Action Name")
    3. ComponentObject.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(...),"Action Name")
    4. ComponentObject.getInputMap().put(KeyStroke.getKeyStroke(...),"Action Name"). (Option 1 and Option 4 are equivalent statements given that Keystroke 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.

Action for mapped key combination Shift F1 on First Name formats text content of 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.

Action for mapped key combination Shift F1 on Middle Name formats text content of Full Name.

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.

Action for mapped key combination Shift F1 on Last Name, formats text content of Full Name.

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.

Key combination Alt Shift F1 triggered on Full Name text field results in text format of text in TextArea component on left pane.

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.

Alt Shift F1 key combination triggered on Address1 field, formats TextArea filed.

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.

Alt Shift F1 key combination triggered on Address2 field, formats TextArea 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 from firstName object of the JTextField component. Without customization, all JTextField objects share Keymap object instance. This will also be shared among all those existing. In the Keymap, corresponding to KeyStroke object for key combination Shift+F1, one customized Action object is mapped.
  • line 123 – 125:  Keymap object is retrieved from fullName object of the JTextField component. Without customization, all JTextField objects share Keymap object instance. This will also be shared among all those existing. In the Keymap, corresponding to KeyStroke object for key combination Alt+Shift+F1, one customized Action 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 from TextAction class. This implementation gives the effect of Shift+F1 key press on the said JTextField 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 InputMapActionMap 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.

Download
You can download the full source code of this example here: KeyBindingExample

Koushik Sanyal

Koushik Sanyal is a resident of a small town Uttarpara, in the suburbs of Kolkata (erstwhile Calcutta) which is one of the biggest cities in India and capital of the state of ‘West Bengal’. He completed Diploma in Information Technology from DoEACC Society (Currently NIELIT), a government of India run autonomous society dedicated for pertaining training in Information Technology. He has spent more than 11 years in IT professional domain as Trainer, Software Developer and Senior Software Developer. Currently he's working as a ‘Senior Software Developer’ in java domain at ‘Greenfield Software Pvt. Ltd in Kolkata, India.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button