Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: janbraun 12. 11. 2016, 09:52:32
-
Potřeboval bych provést nějakou akci, když se stiskne klávesa kdykoliv a kdekoliv v okně. Chci aby se zareagovalo pouze na uvolnění klávesy. Bohužel když je klávesa držená zmáčknutá, tak tak se akce neustále opakuje. Co dělám špatně?
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventDispatcher(new KeyEventDispatcher() {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_RELEASED) {
System.out.println("akce");
}
}
});
-
Chování při držení klávesy je podle mne závislé na platformě (operačním systému). Java ty události jenom přebírá ze systému.
-
Javu neznám, ale evidentně jsi se chytil na "klávesnici" a ne na fyzickou klávesu. Zatímco fyzická klávesa dá jeden event down a jeden up, "klávesnice" jich může vygenerovat mraky (viz třeba autorepeat, pokud klávesu držím).
-
Co dělám špatně?
Používáš Javu. ;D
-
Co dělám špatně?
Používáš Javu. ;D
Tak urcite.
-
Už jsem se s tím setkal. Prostě Xorg posílá takové eventy.
http://bugs.java.com/view_bug.do?bug_id=4153069
http://stackoverflow.com/questions/1736828/how-to-stop-repeated-keypressed-keyreleased-events-in-swing
Tam někdo odkazuje na workaround: http://tech.stolsvik.com/2010/05/linux-java-repeats-released-keyevents.html
-
Na linuxu lze číst eventy z xinput, ale určitě existují i lepší řešení.
-
Když se ti povede nějak z Javy vydobýt X11 handle, tak můžeš zkusit přes JNI zavolat XkbSetDetectableAutoRepeat (https://linux.die.net/man/3/xkbsetdetectableautorepeat).
-
Vyrobil jsem nějaký hack. JNI wrapper je už v knihovně ve formě XlibWrapper, ale to je non-public API.
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test {
public static void main(String[] args) throws Exception {
Program prog = new Program();
SwingUtilities.invokeAndWait(prog);
}
public static class Program implements Runnable {
public Program() {}
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
XHack.SetDetectableAutoRepeat(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new Dispatcher());
}
}
public static class Dispatcher implements KeyEventDispatcher {
public Dispatcher() {}
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_RELEASED)
System.out.println("akce");
return false;
}
}
}
import java.lang.reflect.Method;
import java.lang.Class;
public class XHack {
public static void SetDetectableAutoRepeat(boolean enabled) {
try {
Class<?> xToolkit = Class.forName("sun.awt.X11.XToolkit");
Class<?> xWrapper = Class.forName("sun.awt.X11.XlibWrapper");
Method getDisplay = xToolkit.getDeclaredMethod("getDisplay");
Method setRepeat = xWrapper.getDeclaredMethod("XkbSetDetectableAutoRepeat", Long.TYPE, Boolean.TYPE);
getDisplay.setAccessible(true);
setRepeat .setAccessible(true);
long display = (long) getDisplay.invoke(null);
setRepeat.invoke(null, display, enabled);
} catch (Exception e) {
e.printStackTrace();
}
}
}
U mě to ale nemá žádný efekt, protože se to chová správně i bez toho.
-
Moje domněnka, proč mi to funguje i bez toho:
XlibWrapper.XkbSetDetectableAutoRepeat(..., true) je volán z XToolkit.tryXKB()
XToolkit.tryXKB() je volán z XToolkit.init()
XToolkit.init() je volán z XToolkit.XToolkit()
XToolkit.XToolkit() je volán nepřímo z Toolkit.getDefaultToolkit() pomocí reflexe.
A Toolkit.getDefaultToolkit() je volán např. z EventQueue.invokeAndWait(...)
a ten je konečně volán z SwingUtilities.invokeAndWait(...)
-
Chjo, někdy mám dlouhé vedení... :D
Funguje to, jde to otestovat tak, že místo true použiješ false. Pak u sebe mám stejnou situaci jako ty ve výchozím stavu (tzn. jak to nepotřebuješ).
XHack.SetDetectableAutoRepeat(false);
-
To je proste problem na Xkach, pokud je mi znamo. beznej workarround je hlidat si rozestupy mezi key release a nasledujicim key pressed.