I will present the problem with two simple GUI snippets, one for swing and other for swt, and I will compile and execute them both in windows and linux, using sun jdk version 1.6_13. The snippets are a window that listens to keypressed and keyreleased events and prints a message when the event notification is received. In all the tests, the user will press a key and will keep it pressed for some time (more than a second) and then release it. So the correct output for this "use case" would be only two messages "keyPressed" and
"keyReleased" in the output console. The only toolkit and OS combination that worked as spected was swt+linux. In the rest of the combinations, several "keyPressed" and
"keyReleased" are repeated while the user is keeping the key presssed.
SWT snippet | SWING snippet |
import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class KeyPressTest extends org.eclipse.swt.widgets.Dialog { private Shell dialogShell; public KeyPressTest(Shell parent, int style) { super(parent, style); } public void open() { try { Shell parent = getParent(); dialogShell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); dialogShell.addKeyListener(new KeyListener() { @Override public void keyReleased(KeyEvent arg0) { System.out.println("keyReleased"); } @Override public void keyPressed(KeyEvent arg0) { System.out.println("keyPressed"); } }); dialogShell.setLayout(new FormLayout()); dialogShell.layout(); dialogShell.pack(); dialogShell.setLocation(getParent().toDisplay(100, 100)); dialogShell.open(); Display display = dialogShell.getDisplay(); while (!dialogShell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { try { Display display = Display.getDefault(); Shell shell = new Shell(display); KeyPressTest inst = new KeyPressTest(shell, SWT.NULL); inst.open(); } catch (Exception e) { e.printStackTrace(); } } } | import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; public class KeyPressTest extends javax.swing.JFrame { public KeyPressTest() { super(); initGUI(); } private void initGUI() { try { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); this.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { System.out.println("keyReleased"); } @Override public void keyPressed(KeyEvent e) { System.out.println("keyPressed"); } }); pack(); setSize(400, 300); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { KeyPressTest inst = new KeyPressTest(); inst.setLocationRelativeTo(null); inst.setVisible(true); } }); } } |
And the results, for swing and swt in combination with windows and linux are:
windows | linux | |
swing | keyPressed keyReleased keyPressed keyReleased keyPressed keyReleased .... keyPressed keyReleased | keyPressed keyReleased keyPressed keyReleased keyPressed keyReleased .... keyPressed keyReleased |
SWT | keyPressed keyReleased keyPressed keyReleased keyPressed keyReleased .... keyPressed keyReleased | keyPressed keyPressed keyPressed keyPressed keyPressed ... keyPressed keyPressed keyReleased |
As I understand, THIS IS A BUG, because the behaviour does not apply the documentation. For example, this is the more relevant paragraph about keypressed and keyreleased events of swing (java.awt.event.KeyEvent javadoc):
"Key pressed" and "key released" events are lower-level and depend on the platform and keyboard layout. They are generated whenever a key is pressed or released, and are the only way to find out about keys that don't generate character input (e.g., action keys, modifier keys, etc.).
and this is not true because keyreleased events are generated without a key being released
Note that the only different situation is swt in linux. Imho it is still incorrect (because several keypressed events are fired but only one actually happens) but in this case we can we can know when the key is really releaed. In the other scenarios it is impossible.
IMHO, a cause of this problem coud be that swt and swing designers have modeled the keyboard event system thinking on text and not in other keyboard usage. In the case of swt that heavily relies on underlying plafform (gtk in linux, winapi in windows), I think the event stuff is a responsability of the uderlying platform and it is there where we note the defferencies.
So my question for java developers reading this is if there exists a way of being correctly notified when a keyrelease event occurs using swing or swt. Other question could be if there exists other library for java which handles this correctly because now, I havent figure out how to do this simple task in java ;(.
If you are a java programmer and want a workaround, I think the only one is the following. Since, while the key is pressed the keypressed event is fired regularly, the way we have to know that a key is released is when keypressed event is not firing anymore for that key. Note that this will be incorrect (because it is not really a keyrelease event) and can be expensive to implement because it because it require, for each key, a listener thread that triggers the fake keyrelease event when it detects no more keypressed. My proposal is the following keylistener (KeyListener2 inner class):
package org.sgx.javatests.keypress.swingAll;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class SwingSollutionAllClasses {
/**
* this class is responsible for maintaining a key status map
* @author sebastian
*
*/
static class InputManager implements KeyListener {
//sgurin : þe last keypressed and keyreleased events registered for each key
Map lastKeyPressedEvents = new HashMap();
Map lastKeyReleasedEvents = new HashMap();
//one for each ascii character.
public boolean[] key_state_up = new boolean[256]; //true if pressed
public boolean[] key_state_down = new boolean[256]; //true if not pressed
//a string used as a buffer by widgets or other text input controls
private String keyCache = "";
public InputManager(){
for (int i = 0; i < key_state_up.length; i++) {
key_state_up[i]=true;
key_state_down[i]=false;
}
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if( code >= 0 && code < 256 ) {
key_state_down[code] = true;
key_state_up[code] = false;
lastKeyPressedEvents.put(code, e);
}
}
public void keyReleased(KeyEvent e) {
int code = e.getKeyCode();
if( code >= 0 && code < 256 ) {
key_state_up[code] = true;
key_state_down[code] = false;
lastKeyReleasedEvents.put(code, e);
}
}
public void keyTyped(KeyEvent e) {
keyCache += e.getKeyChar();
}
public boolean isKeyDown( int key ) {
return key_state_down[key];
}
public boolean isKeyUp( int key ) {
return key_state_up[key];
}
}
/**
* this class is a new KeyListener adapter that corrects the default keyreleased
* notification policy (repeatedly notifies keypressed and keyreleased events
* when a key is pressed and not released for a while).
*
* note that this is a heavy listener, use only at special cases (when you need different behaviour for keyreleased event handling)
* it uses an InputManager that mantains the key status and starts a thread that checks each key status and notify changes
*
* @author sgurin
*
*/
static abstract class KeyListener2 implements KeyListener {
KeyChecker checkerThread ;
public KeyListener2(Component target) {
super();
checkerThread=new KeyChecker(this, target);
checkerThread.start();
}
public void destroy() {
checkerThread.stopChecking();
}
static class KeyChecker extends Thread implements KeyListener {
private static final long SLEEP_AMOUNT =50;
InputManager iman;
Component target;
volatile private boolean stoped;
public boolean[] last_key_state_up=null, last_key_state_down=null;
KeyListener2 listener;
public KeyChecker(KeyListener2 listener, Component target){
iman = new InputManager();
this.target = target;
this.listener = listener;
target.addKeyListener(iman);
target.addKeyListener(this);
}
@Override
public void run() {
last_key_state_down=cloneArray(iman.key_state_down);
last_key_state_up = cloneArray(iman.key_state_up);
while(!stoped) {
try {
Thread.sleep(SLEEP_AMOUNT);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < last_key_state_up.length; i++) {
if(last_key_state_up[i] && iman.key_state_down[i])
listener.keyPressed(iman.lastKeyPressedEvents.get(i));
if(last_key_state_down[i] && iman.key_state_up[i])
listener.keyReleased(iman.lastKeyReleasedEvents.get(i));
}
last_key_state_down=cloneArray(iman.key_state_down);
last_key_state_up = cloneArray(iman.key_state_up);
}
}
private boolean[] cloneArray(boolean[] a) {
if(a==null)
return null;
boolean [] r = new boolean[a.length];
for (int i = 0; i < r.length; i++)
r[i]=a[i];
return r;
}
public void stopChecking() {
stoped=true;
}
@Override
public void keyPressed(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
listener.keyTyped(e);
}
}
}
/**
* swing test
*/
static class KeyPressTest extends javax.swing.JFrame {
public KeyPressTest() {
super();
initGUI();
}
private void initGUI() {
try {
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
new KeyListener2(this) {
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased");
}
@Override
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed");
}
};
pack();
setSize(400, 300);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
KeyPressTest inst = new KeyPressTest();
inst.setLocationRelativeTo(null);
inst.setVisible(true);
}
});
}
}
public static void main(String[] args) {
KeyPressTest.main(args);
}
}
Related information I found:
http://stackoverflow.com/questions/1736828/how-to-stop-repeated-keypressed-keyreleased-events-in-swing
http://groups.google.com/group/comp.lang.java.gui/browse_thread/thread/e215f9729154511e
http://gpsnippets.blogspot.com/2008/03/keyboard-input-polling-system-in-java.html
7 comentarios:
Nice dispatch and this enter helped me alot in my college assignement. Thank you seeking your information.
Brim over I to but I contemplate the list inform should have more info then it has.
It is time to become reasonable. It is time to come in itself.
Just wondering if eBay makes it possible for you to market [url=http://www.ticketchoice.com.au]concert tickets[/url] on the net? Do you know if you will find any restrictions depending on what country you're in?
My parents have just called me and asked if i could "get rid" of their two tickets to some concert as they wont have the ability to make it due to one more family event.
Besides asking close friends etc, i thought ebay would be an excellent place to market them.
But whats ebay's policy on marketing tickets? Ive heard alot about it around the news but ive forgotten what happened.
and if it matters, the concert is inside of this coming month
Thanks ahead of time for the advice.
Publicar un comentario