Below is the code I'm using to add an OnKeyboardActionListener to my KeyboardView. (For the sake of brevity, I've omitted the required overridden methods that I've left empty.)
keyboardView.setOnKeyboardActionListener(new OnKeyboardActionListener() {
private void shiftOn(boolean on) {
System.out.println("Shifting " + (on ? "on" : "off"));
keyboard.setShifted(on);
shiftKey.icon = keyboard.isShifted() ? shiftLockDrawable : shiftDrawable;
}
#Override
public void onKey(int primaryCode, int[] keyCodes) {
Editable editable = editText.getEditableText();
int selectionStart = editText.getSelectionStart();
if (primaryCode == SHIFT) {
shiftOn(!keyboard.isShifted());
} else {
if (primaryCode == DELETE) {
if (editable != null && selectionStart > 0) {
editable.delete(selectionStart - 1, selectionStart);
}
} else {
editable.insert(selectionStart, Character.toString((char) primaryCode));
}
shiftOn(false);
}
}
});
The problem
When I press the shift key, everything goes as expected; both the key's icon and the state of "shiftedness" changes.
However, when I press any other key (which is supposed to turn shift off), the state of shiftedness changes to off, but the icon doesn't change to the unshifted version. I experience the same problem when using text instead of icons.
I've tried calling postInvalidate() on my KeyboardView but to no avail.
Here's a video that highlights my problem.
I added the following code to the end of the shiftOn method:
keyboard.invalidateKey(SHIFT);
It seems to me that the redrawing of the shift key is attached to the actual input event. Can you, instead of changing the shiftKeyIcon simulate a shift key press in code? You just generate an additional shift key press event when another key is pressed.
I hope this helps you.
I think that there is a problem in shiftOn(). I don't exactly know what shiftLockDrawable and shiftDrawable do, but maybe the following code works
private void shiftOn(boolean on) {
System.out.println("Shifting " + (on ? "on" : "off"));
shiftKey.icon = (on ? shiftLockDrawable : shiftDrawable);
keyboard.setShifted(on);
}
If this doesn't work or if this isn't what you want, could you maybe provide more information?
Related
I'm trying to make a 2d game using java microedition and i just want to make my control smoother but the problem is when i press the LEFT key while pressing the UP Key the condition is not working i dont know why
public void moveJet2() throws IOException{
int gameAction = getKeyStates();
if(gameAction==LEFT_PRESSED && gameAction==UP_PRESSED){
padX-=padXVel;
padY-=padYVel;
}
else if(gameAction==RIGHT_PRESSED){
padX += padXVel;
}
else if(gameAction==UP_PRESSED){
padY-=padYVel;
}
else if(gameAction==DOWN_PRESSED){
padY+=padYVel;
}
}
getKeyStates() returns the state of keys in a single int. The various keys have individual values. UP_PRESSED = 0x0002 and LEFT_PRESSED = 0x0004. So if you press UP on your d-pad while calling getKeyStates(), you'll get 2 back, and if (getKeyStates()==UP_PRESSED) will thus be true.
Likewise, if you press LEFT on your d-pad while calling getKeyStates(), you'll get 4 back.
But if you press UP and LEFT at the same time, you can't get back 2 and 4 - because that's obviously 2 ints - and getKeyStates() only returns one int.
What you do get back though, is rather simple: 2 + 4 = 6.
So, asking if (getKeyStates()==6) will be true if pressing UP and LEFT at the same time. Or if (getKeyStates()==UP_PRESSED+LEFT_PRESSED).
Typically though, you would ask using bit-operators, like this:
public void moveJet2() throws IOException{
int gameAction = getKeyStates();
if((gameAction & LEFT_PRESSED)!=0) {
padX -= padXVel;
}
if((gameAction & RIGHT_PRESSED)!=0) {
padX += padXVel;
}
if((gameAction & UP_PRESSED)!=0) {
padY-=padYVel;
}
if((gameAction & DOWN_PRESSED)!=0){
padY+=padYVel;
}
}
Because using that approach will work with any of the 8 directions you can press at the same time.
I'm developing a Java/Swing application, and I'm making my own handling for key events using a KeyListener on my JFrame.
My problem is, the key repeat feature of the OS is causing multiple keyPressed events to occur when I hold down a key, when I would like to only receive one.
One solution would be to keep the states of the keys in an array, and only accept the event if the state changes.
private boolean keysDown[] = new boolean[0x10000];
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (0 <= key && key <= 0xFFFF) {
if (keysDown[key]) return;
keysDown[key] = true;
}
// ...
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (0 <= key && key <= 0xFFFF) {
if (!keysDown[key]) return;
keysDown[key] = false;
}
// ...
}
This works, but is very clumsy, and while I only seem to find keycodes in the range 0 to 216-1, I'm not sure if ones outside that range can exist. (getKeyCode() returns int.) Another problem is that pressing down a key, releasing it while in another window, and pressing it again in my application would not register the event.
So, is there a better way to either
disable key repeat in my application or
detect repeated events reliably?
Replace your boolean array with HashSet<Integer> or TreeSet<Integer>. The key repeat is a function of the OS, so there is no way to disable it, only account for it.
How can I let my custom KeyListener listen for combinations of ALT (or CTRL for that matter) + more than one other key?
Assume I have 11 different actions I want the application to do, depending on a combination of keys pressed. ALT + 0 - ALT + 9 obviously don't pose any problems, whereas for ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar) I cannot find a good solution anywhere on the web (or in my head). I'm not convinced that this solution with a timer is the only possible way.
Thanks a million in advance for any suggestions!
Edit: Actions 0-9 + action 10 = 11 actions. Thanks #X-Zero.
You should not use KeyListener for this type of interaction. Instead use key bindings, which you can read about in the Java Tutorial. Then you can use the InputEvent mask to represent when the various modifier keys are depresed. For example:
// Component that you want listening to your key
JComponent component = ...;
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK),
"actionMapKey");
component.getActionMap().put("actionMapKey",
someAction);
See the javadoc for KeyStroke for the different codes you can use while getting the KeyStroke. These modifiers can be OR'ed together to represent various combinations of keys. Such as
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK
| java.awt.event.InputEvent.SHIFT_DOWN_MASK)
To represent when the Ctrl + Shift keys were depressed.
Edit: As has been pointed out, this does not answer you question but instead should just be taken as some good advice.
You can use KeyListener for this purpose by combining certain things. Look at the following example, which should be placed in an overridden keyPressed(KeyEvent e) method.
if (e.isControlDown() && e.getKeyChar() != 'a' && e.getKeyCode() == 65) {
System.out.println("Select All");
}
The string Select All will be displayed when Ctrl + a is pressed. The method e.isControlDown() checks whether the Ctrl key is pressed or not.
Similarly, the Alt key combinations can be done with the same method by using e.isAltDown() method.
The logic behind this is, e.getKeyChar() returns the character of the key presses & e.getKeyCode() returns its ASCII code. When Ctrl is pressed and hold, the e.getKeyChar() won't return a and e.getKeyCode() will be the same 65. Hope you understand this. Feel free to ask more.
ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar)
seems to clash with (from one of your comments):
So for example if the user wants to change data in column 11 (which would be called "10"), s/he'd press ALT + 1 + [lets go of both ALT and 1] 0.
Assuming that ALT+10 means 'Pressing ALT, pressing and releasing 1, pressing and releasing 0, releasing ALT' I propose trying this:
In keyPressed, listening for the ALT key, activate a boolean flag, isAltPressed, and create a buffer to hold key presses that occur (a string, say).
In keyTyped, if isAltPressed is active, append key codes to the buffer.
In keyReleased, listening for ALT again, open a conditional querying the buffer and executing actions.
public void keyPressed (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
buffer = ""; //declared globally
isAltPressed = true; } //declared globally
}
public void keyTyped (KeyEvent e){
if (isAltPressed)
buffer.append (e.getKeyChar());
}
public void keyReleased (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
isAltPressed = false;
if (buffer.equals (4948)) //for pressing "1" and then "0"
doAction();
else if (buffer.equals(...))
doOtherAction();
...
}//if alt
}
import java.awt.*;
import java.awt.event.*;
class KDemo
{
public static void main(String args[])
{
Frame f = new Frame();
f.setSize(500,500);
f.setVisible(true);
f.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
AWTKeyStroke ak = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
if(ak.equals(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_F4,InputEvent.ALT_MASK)))
{
System.exit(0);
}
}
});
}
}
I would suggest that instead of using key combinations, consider some input field when the window isVisible() and is focused. The field can be hidden, like Windows' File explorer hidden filename search (enter a directory, type the filename and the correspondence is focused), or is visible, like in Ubuntu.
Key combinations are not designed for including more than one key other than modifiers, although you may be able to achieve this.
I think there's a simpler way which I am using. If the KeyEvent is ev then if you investigate:
(int)ev.getKeyChar()
you find that ctrl-a is 1, ctrl-b is 2 and so on. I wanted to use ctrl-s for save. So I just use:
(((int)ev.getKeyChar())==19)
to detect it. No idea why, but it works fine whereas:
ev.isControlDown() && ev.getKeyChar()=='s'
does not.
How can I let my custom KeyListener listen for combinations of ALT (or CTRL for that matter) + more than one other key?
Assume I have 11 different actions I want the application to do, depending on a combination of keys pressed. ALT + 0 - ALT + 9 obviously don't pose any problems, whereas for ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar) I cannot find a good solution anywhere on the web (or in my head). I'm not convinced that this solution with a timer is the only possible way.
Thanks a million in advance for any suggestions!
Edit: Actions 0-9 + action 10 = 11 actions. Thanks #X-Zero.
You should not use KeyListener for this type of interaction. Instead use key bindings, which you can read about in the Java Tutorial. Then you can use the InputEvent mask to represent when the various modifier keys are depresed. For example:
// Component that you want listening to your key
JComponent component = ...;
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK),
"actionMapKey");
component.getActionMap().put("actionMapKey",
someAction);
See the javadoc for KeyStroke for the different codes you can use while getting the KeyStroke. These modifiers can be OR'ed together to represent various combinations of keys. Such as
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK
| java.awt.event.InputEvent.SHIFT_DOWN_MASK)
To represent when the Ctrl + Shift keys were depressed.
Edit: As has been pointed out, this does not answer you question but instead should just be taken as some good advice.
You can use KeyListener for this purpose by combining certain things. Look at the following example, which should be placed in an overridden keyPressed(KeyEvent e) method.
if (e.isControlDown() && e.getKeyChar() != 'a' && e.getKeyCode() == 65) {
System.out.println("Select All");
}
The string Select All will be displayed when Ctrl + a is pressed. The method e.isControlDown() checks whether the Ctrl key is pressed or not.
Similarly, the Alt key combinations can be done with the same method by using e.isAltDown() method.
The logic behind this is, e.getKeyChar() returns the character of the key presses & e.getKeyCode() returns its ASCII code. When Ctrl is pressed and hold, the e.getKeyChar() won't return a and e.getKeyCode() will be the same 65. Hope you understand this. Feel free to ask more.
ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar)
seems to clash with (from one of your comments):
So for example if the user wants to change data in column 11 (which would be called "10"), s/he'd press ALT + 1 + [lets go of both ALT and 1] 0.
Assuming that ALT+10 means 'Pressing ALT, pressing and releasing 1, pressing and releasing 0, releasing ALT' I propose trying this:
In keyPressed, listening for the ALT key, activate a boolean flag, isAltPressed, and create a buffer to hold key presses that occur (a string, say).
In keyTyped, if isAltPressed is active, append key codes to the buffer.
In keyReleased, listening for ALT again, open a conditional querying the buffer and executing actions.
public void keyPressed (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
buffer = ""; //declared globally
isAltPressed = true; } //declared globally
}
public void keyTyped (KeyEvent e){
if (isAltPressed)
buffer.append (e.getKeyChar());
}
public void keyReleased (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
isAltPressed = false;
if (buffer.equals (4948)) //for pressing "1" and then "0"
doAction();
else if (buffer.equals(...))
doOtherAction();
...
}//if alt
}
import java.awt.*;
import java.awt.event.*;
class KDemo
{
public static void main(String args[])
{
Frame f = new Frame();
f.setSize(500,500);
f.setVisible(true);
f.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
AWTKeyStroke ak = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
if(ak.equals(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_F4,InputEvent.ALT_MASK)))
{
System.exit(0);
}
}
});
}
}
I would suggest that instead of using key combinations, consider some input field when the window isVisible() and is focused. The field can be hidden, like Windows' File explorer hidden filename search (enter a directory, type the filename and the correspondence is focused), or is visible, like in Ubuntu.
Key combinations are not designed for including more than one key other than modifiers, although you may be able to achieve this.
I think there's a simpler way which I am using. If the KeyEvent is ev then if you investigate:
(int)ev.getKeyChar()
you find that ctrl-a is 1, ctrl-b is 2 and so on. I wanted to use ctrl-s for save. So I just use:
(((int)ev.getKeyChar())==19)
to detect it. No idea why, but it works fine whereas:
ev.isControlDown() && ev.getKeyChar()=='s'
does not.
I am building a GWT component to behave much like the comments box here on stackoverflow, and other sites. I am trying to register listeners for KeyPress, Change and ONPASTE events that will update my status line with number of characters remaining, etc.
It works except it is always one character behind the actual number of characters in the text area. I set the max number of characters to 10. When I type the first character it still says, "10 characters remaining". It doesn't update the status line until I type the second character and then it is one off, it says 9 characters remaining when the second character is typed.
When I BACKSPACE or DELETE, it is also one off, when there are no characters it still says "9 characters remaining" until I press the BACKSPACE or DELETE a second time.
I am getting this behavior in both Firefox, Chrome and Internet Explorer on Windows. So I think I am not registering something correctly.
I know this has something to do with when the events are getting fired, but I have spend hours on trying to diagnose this behavior and have run out of ideas.
Here is where I am registering the event handlers, the complete code is BoundedTextAreaWithFeedback.
private void registerHandlers()
{
final BoundedTextAreaWithFeedback outer = this;
this.textArea.addChangeHandler(new ChangeHandler()
{
public void onChange(final ChangeEvent changeEvent)
{
outer.validate();
}
});
this.textArea.addKeyPressHandler(new KeyPressHandler()
{
public void onKeyPress(final KeyPressEvent keyPressEvent)
{
outer.validate();
}
});
this.panel.addFocusHandler(new FocusHandler()
{
public void onFocus(final FocusEvent focusEvent)
{
outer.textArea.setFocus(true);
}
});
// capture paste events
this.textArea.sinkEvents(Event.ONPASTE);
}
Here is the validate() method.
private boolean validate()
{
final boolean isValid;
final int len = this.textArea.getText().length();
if (len < this.minLength)
{
this.status.setText("Enter at least " + this.minLength + " characters.");
this.status.setStyleName("input-status-underflow");
isValid = false;
}
else if (len > this.maxLength)
{
this.status.setText(this.maxLength - len + " characters remaining");
this.status.setStyleName("input-status-overflow");
isValid = false;
}
else
{
this.status.setText(this.maxLength - len + " characters remaining");
this.status.setStyleName("input-status-ok");
isValid = true;
}
return isValid;
}
I just started adding every addXXXHandler() until one worked.
this.textArea.addKeyUpHandler(new KeyUpHandler()
{
public void onKeyUp(final KeyUpEvent event)
{
outer.validate();
}
});
Seems to have done the trick.
Here is the working code, CTRL-V and paste from context menu also work now.
Try using a DeferredCommand to execute the validation code. I believe the problem is that when the event is firing, they character is not yet added to the text area. The DeferredCommand will not execute until any pending event handlers have finished, allowing the length of the text to be calculated correctly.
See this question for an example of using a DeferredCommand.