I made a custom JTextField to input numeric values. The class looks as follows:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
public class JNumberField extends JTextField {
public JNumberField(int width, int height) {
this();
setPreferredSize(new Dimension(width, height));
}
public JNumberField() {
setFont(new Font("Monospace", Font.BOLD, 23));
setHorizontalAlignment(JTextField.HORIZONTAL);
}
#Override
public void processKeyEvent(KeyEvent ev) {
//Accepts digits, backspace, minus key and left/right arrow keys
if (Character.isDigit(ev.getKeyChar()) || ev.getKeyCode() == KeyEvent.VK_BACK_SPACE || ev.getKeyCode() == KeyEvent.VK_MINUS || ev.getKeyCode() == KeyEvent.VK_LEFT || ev.getKeyCode() == KeyEvent.VK_RIGHT) {
super.processKeyEvent(ev);
}
ev.consume();
}
public static void main(String[] args) {
JOptionPane.showInputDialog(new JNumberField(300,50));
}
}
All the keys are working except for the "-" key. I also printed the keycode that was pressed and it responded with 45 so the if statement is executed but it doesn't insert the - into the text field. Is this a bug or how can it be fixed? I also tried removing the if statement and just call the processKeyEvent method and then it works to insert the - sign...
First of all I totally agree with Andrew, there are better solutions to solving the problem. For example you can use:
a JSpinner
a JFormattedTextField
a DocumentFilter - see: How to automatically update small letter characters to capital letter in Java JEditorPane?
The reason the above solutions are better is because:
you don't need to extend a class
the solutions are more abstract
Your solution is a low level solution, which means you need to understands the events that are generated when you press a key and how those events are handled by Swing.
A more abstract solution is always better since you don't need to know the details. This is why the Swing API has evolved.
In your case you are not understanding the difference between a "displayable" key event and a "non displayable" event.
A text component is updated when the keyTyped event is generated which is only generated for "displayable" character like " numeric digits" and "-".
A "keyTyped" event is not generated for using the arrow keys or backspace keys.
So your logic needs to be changed to handle the "-" as a character (not a key code):
//if (Character.isDigit(ev.getKeyChar()) || ev.getKeyCode() == KeyEvent.VK_MINUS ...
if (Character.isDigit(ev.getKeyChar()) || ev.getKeyChar() == '-' ...
Related
I have a requirement where need to mask the value entering into jText area at runtime. I am able to achieve this but problem is scenario with backspace. When I press back space sequentially (one by one) then it work while if I kept pressing then its counting the event as one and removing only one character (by considering the key event as one key released).
Here my code snippets is :
public void showPrompt() throws InterruptedException {
sem.acquire();
this.toFront();
this.setAlwaysOnTop(true);
this.setVisible(true);
if(encryptKeystroke == true) {
jTextArea.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
if (e.getExtendedKeyCode() == KeyEvent.VK_BACK_SPACE) {
text = text.substring(0, text.length() - 1);
}
else {
text += String.valueOf(e.getKeyChar());
}
jTextArea.setText(text.replaceAll(".", "*"));
}
});
}
}
Is there any way if I kept pressing the backspace then it should remove all the characters irrespective of considering it one key event ?
As you have said in the comment that the requirements are not exactly like the password and so you won't be using JPasswordField, I would like to give a solution for the question.
Here, the code to detect a backspace key stroke is written inside the keyReleased() method. Now, the keyReleased() method will be called by the keyListener when you will pull your finger up from a key in this case your backspace key. That is why even if you continuously keep pressing the backspace key, it will execute the code only once i.e. only when you release the key.
Now, you wish to remove one character every time backspace is pressed so you can just move your code from the keyReleased() method to the keyPressed() method.
If you move the code inside the keyPressed() method, then the code will be executed at every key stroke even if you continuously keep pressing the backspace key.
We are replacing a legacy C application originally written for MSDOS (yes, believe it or not!). This application uses a specially remapped keyboard which intercepts the DOS keyboard interrupt (remember that??!) to sometimes alter the scan codes of the keys pressed by the user so that different processing would occur. Special labels were then placed on the keys telling the users the "new" meaning of these keys.
The new Java version is required to preserve this keyboard layout which the targeted group of users is very familiar with.
An example of what we are trying to do is as follows:
You may never have thought about this, but the numeric keypad of a modern telephone is reversed from the numeric keypad of a computer keyboard. On the former 1-2-3 is on the top row and on the latter it is on the bottom row. We are required to make the keyboard's numeric keypad look like the telephone. Let's say, when the user types "7" on the numeric keypad, we want it look as though he typed a "1", when he types an "8", we want a "2", when he types a "3" we want a "9".
There is much more that we have to do to emulate the DOS application, but we can't even solve this simple case now. I have been all over Key Binding, KeyAdapters, KeyListeners, and even KeyEventDispatchers, and I cannot make this work. I am pretty sure we have to work on the lowest level we are allowed to work by Java to come as close as possible to what the legacy app does. And needless to say, we want the cleanest implementation possible, so that the application level code is not littered with inputMaps and actionMaps etc. As much as possible this needs to be handled globally. Can anyone help?
If I were doing this, I would write the Java App without worrying about the key bindings. Assume that when a component gets a keyevent for #7 its #7, don't worry about whether the 7 or 1 was really typed. The app shouldn't care about how keys are mapped on the keyboard. This should let you start developing the app immediately.
As far as overriding key bindings, this seems like where you want to look: http://download.oracle.com/javase/7/docs/api/java/awt/KeyEventDispatcher.html
It sounds like you can write your own KeyEventDispatcher to handle all the key mapping logic and it prevents mapping logic from messing up the rest of the logic in your application.
This is hacky, and I'll admit I haven't used it myself, but you could subclass KeyEvent and override the fields as needed. So yourSubclass.VK_NUMPAD1 is the integer value of KeyEvent.VK_NUMPAD7, yourSubclass.VK_NUMPAD2 is the integer value of KeyEvent.VK_NUMPAD8, etcetera. Then use your subclass everywhere KeyEvent is normally used.
I also agree that the KeyEventDispatcher should work. But if this a version/platform issue, then maybe you can use a custom Event Queue. See Global Event Dispatching
My stack overfloweth!
The answer was here:
http://download.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html
which says:
For key pressed and key released
events, the getKeyCode method returns
the event's keyCode. For key typed
events, the getKeyCode method always
returns VK_UNDEFINED.
My original attempt thought it could get a keyCode on KEY_TYPED. It could not, and it was the KEY_TYPED event that was clobbering the mapping done in KEY_PRESSED.
Here is a working implementation:
import static java.awt.event.KeyEvent.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
public class KeyboardDispatcherDemo extends JFrame {
/**
* This class shows how to map numeric keypad keys.
* It performs two conversions:
* 1. lower-to-uppercase
* 2. if numeric keypad 7-9 is typed, 1-3 appears and vice versa.
*
* This is modified from the code at
* http://www.exampledepot.com/egs/java.awt/DispatchKey.html#comment-51807
* which demoes the lower-to-upper conversion.
*
* It doesn't yet handle modified numeric keypad keys.
*
*/
public KeyboardDispatcherDemo() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
new KeyEventDispatcher() {
private char lastMappedKey;
private final Map<Integer, Character> keyMap =
new HashMap<Integer, Character>() {
{
put(VK_NUMPAD1, '7');
put(VK_NUMPAD2, '8');
put(VK_NUMPAD3, '9');
put(VK_NUMPAD7, '1');
put(VK_NUMPAD8, '2');
put(VK_NUMPAD9, '3');
}};
public boolean dispatchKeyEvent(KeyEvent e) {
System.out.println(String.format("INPUT: %s", e.toString()));
boolean dispatch = false;
switch (e.getID()) {
case KeyEvent.KEY_PRESSED:
dispatch = dispatchKeyPressed(e);
break;
case KeyEvent.KEY_TYPED:
dispatch = dispatchKeyTyped(e);
break;
case KeyEvent.KEY_RELEASED:
dispatch = dispatchKeyReleased(e);
break;
default:
throw new IllegalArgumentException();
}
System.out.println(String.format("OUTPUT: %s", e.toString()));
System.out.println();
return dispatch;
}
private boolean dispatchKeyPressed(KeyEvent e) {
char k = e.getKeyChar();
if (k != CHAR_UNDEFINED) {
if (Character.isLetter(k)) {
e.setKeyChar(Character.toUpperCase(e.getKeyChar()));
} else if (e.getModifiers() == 0){
Character mapping = keyMap.get(e.getKeyCode());
if (mapping != null) {
e.setKeyChar(mapping);
}
}
// save the last mapping so that KEY_TYPED can use it.
// note we don't do this for modifier keys.
this.lastMappedKey = e.getKeyChar();
}
return false;
}
// KEY_TYPED events don't have keyCodes so we rely on the
// lastMappedKey that was saved on KeyPressed.
private boolean dispatchKeyTyped(KeyEvent e) {
char k = e.getKeyChar();
if (k != CHAR_UNDEFINED) {
e.setKeyChar(lastMappedKey);
}
return false;
}
private boolean dispatchKeyReleased(KeyEvent e) {
char k = e.getKeyChar();
if (k != CHAR_UNDEFINED) {
e.setKeyChar(lastMappedKey);
this.lastMappedKey=CHAR_UNDEFINED;
}
return false;
}
});
setTitle("KeyboardDispatcherDemo");
JPanel panel = new JPanel();
panel.setBackground(new Color(204, 153, 255));
panel.setLayout(new BorderLayout());
getContentPane().add(panel, BorderLayout.CENTER);
JTextArea staticText = new JTextArea();
staticText.setText("This demonstrates how to map numeric keypad keys. It uppercases all letters and converts Numeric Keypad 1-3 to 7-9 and vice versa. Try it.");
staticText.setLineWrap(true);
staticText.setWrapStyleWord(true);
panel.add(staticText, BorderLayout.NORTH);
staticText.setFocusable(false);
JTextField textField = new JTextField();
textField.setText("");
textField.setHorizontalAlignment(SwingConstants.LEFT);
panel.add(textField, BorderLayout.SOUTH);
textField.setColumns(10);
textField.setFocusable(true);
setSize(getPreferredSize());
setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
new KeyboardDispatcherDemo();
}
#Override
public Dimension getPreferredSize() {
// TODO Auto-generated method stub
return new Dimension(400,300);
}
}
Thanks to all who nudged me toward the answer.
which brings me to my next question ... stay tuned.
I have written an SWT UI which has a primary function of displaying text in a StyledText control. I want to add a handler for Ctrl+F so that when that shortcut is pressed the focus is set to a search box. I have tried using the following code to detect the keypress.
sWindow = new Shell();
...
sWindow.getDisplay().addFilter(SWT.KeyDown, new Listener()
{
#Override
public void handleEvent(Event e)
{
System.out.println("Filter-ctrl: " + SWT.CTRL);
System.out.println("Filter-mask: " + e.stateMask);
System.out.println("Filter-char: " + e.character);
}
});
I was expecting that when I pressed Ctrl+f I would see the following output:
Filter-ctrl: 262144
Filter-mask: 262144
Filter-char: f
However, in practice I actually see the following.
Filter-ctrl: 262144
Filter-mask: 262144
Filter-char: <unprintable char - displayed as a box in eclipse console>
I have two questions:
Is Display.addFilter(...) the best way to add a global shortcut? I tried Display.addListener(...) but this didn't receive any events at all.
Why don't I get the pressed character when I'm holding down Ctrl? When I hold down alt or shift I get the expected mask and the pressed character.
Is Display.addFilter(...) the best way to add a glbal shortcut? I tried Display.addListener(...) but this didn't receive any events at all.
Yes, normally Display.addFilter(...) is the best way to add a glbal shortcut because they have higher preference over the event listeners. See the below comment from Display.addFilter(...) javadoc.
Because event filters run before other
listeners, event filters can both
block other listeners and set
arbitrary fields within an event. For
this reason, event filters are both
powerful and dangerous. They should
generally be avoided for performance,
debugging and code maintenance
reasons.
For your second question:
Why don't I get the pressed character when I'm holding down ctrl? When I hold down alt or shift I get the expected mask and the pressed character.
The problem is that you are looking at the wrong place. Instead of querying e.character you should be using e.keyCode. As per javadoc of e.character you won't get just character f:
Depending on the event, the character
represented by the key that was typed.
This is the final character that
results after all modifiers have been
applied. For example, when the user
types Ctrl+A, the character value is
0x01 (ASCII SOH).
So when you press CTRL+f then it converts in 0x06 (ASCII ACK). Which is not the case when you do ALT+f or SHIFT+f.
On the other hand the javadoc of e.keyCode says:
depending on the event, the key code
of the key that was typed, as defined
by the key code constants in class
SWT. When the character field of the
event is ambiguous, this field
contains the unaffected value of the
original character. For example,
typing Ctrl+M or Enter both result in
the character '\r' but the keyCode
field will also contain '\r' when
Enter was typed and 'm' when Ctrl+M
was typed.
Check the below code for more details. For demo I have tried to put listener on Display and Test.
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class ControlF
{
public static void main(String[] args)
{
Display display = new Display ();
final Shell shell = new Shell (display);
final Color green = display.getSystemColor (SWT.COLOR_GREEN);
final Color orig = shell.getBackground();
display.addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(Event e) {
if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f'))
{
System.out.println("From Display I am the Key down !!" + e.keyCode);
}
}
});
shell.addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent e) {
if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f'))
{
shell.setBackground(orig);
System.out.println("Key up !!");
}
}
public void keyPressed(KeyEvent e) {
if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f'))
{
shell.setBackground(green);
System.out.println("Key down !!");
}
}
});
shell.setSize (200, 200);
shell.open ();
while (!shell.isDisposed()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
}
In Java, when a key is pressed such as a letter, how do I prevent the key from outputting the letter that it is assigned to a jTextPane? (Similar to how do game developers suppress the normal functions of the keyboard when a part of their application is in focus).
When KeyEvent.consume() doesn't do the job alone, is there another way?
I'm a fairly novice programmer compared to other people on this board, so please be patient with me. Any examples would be appreciated. I'm eager to learn. Thank you very much.
Assign custom DocumentFilter to the document from the JTextPane. You can intercept the insertString() and skip unnecessary input. It's better than key listener if you should also skip the same chars from pasted content.
This code is just a sample, I hope this will prevent A to Z getting entered , but does not cover all scenarios such as Shift,Ctrl and Alt presses.
JTextPane textPane = new JTextPane();
textPane.addKeyListener(new MyKeyListener());
public class MyKeyListener extends KeyAdapter
{
public void keyPressed(KeyEvent key) {
int i = key.getKeyCode();
if (i >= 65 && i <= 90)
{
((JTextPane)event.getSource()).cancelKey();
}
}
}
The JTextField is a calculator display initialized to zero and it is bad form to display a decimal number with a leading 0 like 0123 or 00123. The numeric buttons (0..9) in a NetBeans Swing JFrame use append() [below] to drop the leading zeros, but the user may prefer the keyboard to a mouse, and non-numeric characters also need to be handled.
private void append(String s) {
if (newEntry) {
newEntry = false;
calcDisplay.setText(s);
} else if (0 != Float.parseFloat(calcDisplay.getText().toString())) {
calcDisplay.setText(calcDisplay.getText().toString() + s);
}
}
You could restrict the characters input to the JTextField by adding a custom KeyListener. Here is a quick example to demonstrate the idea:
myTextField.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
char c = e.getKeyChar();
if (!Character.isDigit(c)) {
e.consume(); // Stop the event from propagating.
}
}
});
Of course, you need to consider special keys like Delete and combinations like CTRL-C, so your KeyListener should be more sophisticated. There are probably even freely available utilities to do most of the grunt work for you.
You can do this with DocumentFilter.
(Edit: This answer originally included a ling to a simple complete example program on my now defunct weblog.)