Problems with single-tap and press-and-hold with LWJGL - java

I've been researching a way to use LWJGL for my input system. I'm having problems detecting if it is a single-press or a press-and-hold. The event fires twice when I tap, instead of just once.
while(Keyboard.next())
{
if(Keyboard.getEventKeyState())
{
if(Keyboard.isRepeatEvent())
{
//Key held.
doAction(Keyboard.getEventKey(), true, false);
}
else
{
//Key pressed
doAction(Keyboard.getEventKey(), false, false);
}
}
else
{
//Fired when key is released.
doAction(Keyboard.getEventKey(), false, true);
}
}
Edit: I've both resolved the issue and modified this. Here you go, a modified version. (Dammit, Teamviewer..)
/**
* Updates all mouse info, keys bound, and performs actions.
*/
public static void tick()
{
mouseButtons[0] = Mouse.isButtonDown(0);
mouseButtons[1] = Mouse.isButtonDown(1);
mousePos[0] = Mouse.getX();
mousePos[1] = Mouse.getY();
while(Keyboard.next())
{
doAction(0, false);
if(Keyboard.getEventKeyState())
{
if(!Keyboard.isRepeatEvent())
{
doAction(Keyboard.getEventKey(), false);
}
}
else
{
doAction(Keyboard.getEventKey(), true);
}
}
while(Mouse.next())
{
}
}
/**
* Does the associated action for each key. Called automatically from tick.
* #param key The key to check & perform associated action
*/
public static void doAction(int key, boolean ifReleased)
{
if(mouseButtons[0])
{
}
if(mouseButtons[1])
{
}
if(key == 2 & !ifReleased)
{
System.out.println("a");
}
if(Keyboard.isKeyDown(3))
{
System.out.println("b");
}
}

I know it's been awhile since this question was asked, but I came up with a solution myself. My InputHelper lets you determine if a key or mouse button is pressed, released, or held down, and can be accessed from anyother class without initializing and sharing the same instance of it.
It works by having 2 arrays, 1 for mouse events, 1 for keyboard events, that each store one enum value for each key. If there is a button or key event, when updated, the update function sets the value in the appropriate array for that button/key to a certain enum. Then, the next time it is updated, it sets all the key and button events to no event, and repeats the process, handling any new events.
/*
* Handles mouse and keyboard input and stores values for keys
* down, released, or pressed, that can be accessed from anywhere.
*
* To update the input helper, add this line into the main draw loop:
* InputHelper.update();
*
* Use as so (can be used from anywhere):
* InputHelper.isKeyDown(Keyboard.KEY_SPACE);
*/
import java.util.ArrayList;
import org.lwjgl.input.*;
/**
*
* #author Jocopa3
*/
public class InputHelper {
private static InputHelper input = new InputHelper(); //Singleton class instance
private enum EventState {
NONE,PRESSED,DOWN,RELEASED;
}
private ArrayList<EventState> mouseEvents;
private ArrayList<EventState> keyboardEvents;
public InputHelper(){
//Mouse initialization
mouseEvents = new ArrayList<EventState>();
//Add mouse events to Array list
for(int i = 0; i < Mouse.getButtonCount(); i++) {
mouseEvents.add(EventState.NONE);
}
//Keyboard initialization
keyboardEvents = new ArrayList<EventState>();
//Add keyboard events to Array list
for(int i = 0; i < Keyboard.KEYBOARD_SIZE; i++) {
keyboardEvents.add(EventState.NONE);
}
}
private void Update(){
resetKeys(); //clear Keyboard events
//Set Key down events (more accurate than using repeat-event method)
for(int i = 0; i < Keyboard.KEYBOARD_SIZE;; i++){
if(Keyboard.isKeyDown(i))
keyboardEvents.set(i, EventState.DOWN);
}
while(Keyboard.next()){ //Handle all Keyboard events
int key = Keyboard.getEventKey();
if(key<0) continue; //Ignore no events
if(Keyboard.getEventKeyState()){
if(!Keyboard.isRepeatEvent()){
keyboardEvents.set(key, EventState.PRESSED);
}
}else{
keyboardEvents.set(key, EventState.RELEASED);
}
}
resetMouse(); //clear Mouse events
//Set Mouse down events
for(int i = 0; i < Mouse.getButtonCount(); i++){
if(Mouse.isButtonDown(i))
mouseEvents.set(i, EventState.DOWN);
}
while (Mouse.next()){ //Handle all Mouse events
int button = Mouse.getEventButton();
if(button<0) continue; //Ignore no events
if (Mouse.getEventButtonState()) {
mouseEvents.set(button, EventState.PRESSED);
}else {
mouseEvents.set(button, EventState.RELEASED);
}
}
}
//Set all Keyboard events to false
private void resetKeys(){
for(int i = 0; i < Keyboard.KEYBOARD_SIZE;; i++) {
keyboardEvents.set(i, EventState.NONE);
}
}
//Set all Mouse events to false
private void resetMouse(){
for(int i = 0; i < Mouse.getButtonCount(); i++) {
mouseEvents.set(i, EventState.NONE);
}
}
//Non-static version of methods (Only used in the singleton instance)
private boolean KeyDown(int key){
return keyboardEvents.get(key)==EventState.DOWN;
}
private boolean KeyPressed(int key){
return keyboardEvents.get(key)==EventState.PRESSED;
}
private boolean KeyReleased(int key){
return keyboardEvents.get(key)==EventState.RELEASED;
}
private boolean MouseButtonDown(int key){
return mouseEvents.get(key)==EventState.DOWN;
}
private boolean MouseButtonPressed(int key){
return mouseEvents.get(key)==EventState.PRESSED;
}
private boolean MouseButtonReleased(int key){
return mouseEvents.get(key)==EventState.RELEASED;
}
//Static version of methods (called from anywhere, return singleton instance value)
public static boolean isKeyDown(int key){
return input.KeyDown(key);
}
public static boolean isKeyPressed(int key){
return input.KeyPressed(key);
}
public static boolean isKeyReleased(int key){
return input.KeyReleased(key);
}
public static boolean isButtonDown(int key){
return input.MouseButtonDown(key);
}
public static boolean isButtonPressed(int key){
return input.MouseButtonPressed(key);
}
public static boolean isButtonReleased(int key){
return input.MouseButtonReleased(key);
}
public static void update(){
input.Update();
}
}
It has to be updated every frame manually, so your main draw loop you should add the line InputHelper.update(); like this:
while(!Display.isCloseRequested()) {
InputHelper.update(); //Should go before other code that uses the inputs
//Rest of code here
}
Once you have it setup to update every frame, you can use it anywhere you need to determine input states for Mouse or Key buttons as so:
//Mouse test
if(InputHelper.isButtonPressed(0))
System.out.println("Left Mouse button pressed");
if(InputHelper.isButtonDown(0))
System.out.println("Left Mouse button down");
if(InputHelper.isButtonReleased(0))
System.out.println("Left Mouse button released");
//Keyboard Test
if(InputHelper.isKeyPressed(Keyboard.KEY_SPACE))
System.out.println("Space key pressed");
if(InputHelper.isKeyDown(Keyboard.KEY_SPACE))
System.out.println("Space key down");
if(InputHelper.isKeyReleased(Keyboard.KEY_SPACE))
System.out.println("Space key released");

Related

How do I check a key press in Java?

Basically, I'm learning Java and I'm trying to make a simple game in Netbeans using JavaFX. Right now I have a window set up with a rectangle drawn in it. (I have a canvas set up to draw on.) I have made a player class and global class, but I need to know how to read key inputs. I was taught by a friend who is REALLY good with Java, so the info he gives me is good. I read up on KeyEvent, but I have no clue how to implement it.
Any help is greatly appreciated.
I had the same question some weeks ago. The problem was about how to check at any time if a key is being held down or not. There are various solutions, I solved it by using a bitset:
package game.input;
import java.util.BitSet;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class Input {
/**
* Bitset which registers if any {#link KeyCode} keeps being pressed or if it is released.
*/
private BitSet keyboardBitSet = new BitSet();
// -------------------------------------------------
// default key codes
// will vary when you let the user customize the key codes or when you add support for a 2nd player
// -------------------------------------------------
private KeyCode upKey = KeyCode.UP;
private KeyCode downKey = KeyCode.DOWN;
private KeyCode leftKey = KeyCode.LEFT;
private KeyCode rightKey = KeyCode.RIGHT;
private KeyCode primaryWeaponKey = KeyCode.SPACE;
private KeyCode secondaryWeaponKey = KeyCode.CONTROL;
Scene scene;
public Input( Scene scene) {
this.scene = scene;
}
public void addListeners() {
scene.addEventFilter(KeyEvent.KEY_PRESSED, keyPressedEventHandler);
scene.addEventFilter(KeyEvent.KEY_RELEASED, keyReleasedEventHandler);
}
public void removeListeners() {
scene.removeEventFilter(KeyEvent.KEY_PRESSED, keyPressedEventHandler);
scene.removeEventFilter(KeyEvent.KEY_RELEASED, keyReleasedEventHandler);
}
/**
* "Key Pressed" handler for all input events: register pressed key in the bitset
*/
private EventHandler<KeyEvent> keyPressedEventHandler = new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
// register key down
keyboardBitSet.set(event.getCode().ordinal(), true);
}
};
/**
* "Key Released" handler for all input events: unregister released key in the bitset
*/
private EventHandler<KeyEvent> keyReleasedEventHandler = new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
// register key up
keyboardBitSet.set(event.getCode().ordinal(), false);
}
};
// -------------------------------------------------
// Evaluate bitset of pressed keys and return the player input.
// If direction and its opposite direction are pressed simultaneously, then the direction isn't handled.
// -------------------------------------------------
public boolean isMoveUp() {
return keyboardBitSet.get( upKey.ordinal()) && !keyboardBitSet.get( downKey.ordinal());
}
public boolean isMoveDown() {
return keyboardBitSet.get( downKey.ordinal()) && !keyboardBitSet.get( upKey.ordinal());
}
public boolean isMoveLeft() {
return keyboardBitSet.get( leftKey.ordinal()) && !keyboardBitSet.get( rightKey.ordinal());
}
public boolean isMoveRight() {
return keyboardBitSet.get( rightKey.ordinal()) && !keyboardBitSet.get( leftKey.ordinal());
}
public boolean isFirePrimaryWeapon() {
return keyboardBitSet.get( primaryWeaponKey.ordinal());
}
public boolean isFireSecondaryWeapon() {
return keyboardBitSet.get( secondaryWeaponKey.ordinal());
}
}

JavaFX OnMouseDragged not getting fired when dragging an Item in a Treeview

Bascially I try to implement the behaviour, that when dragging an item from a treeView and going either at the bottom or top of the treeView, the treeView will automatically scroll down or up.
Thus far I was able to simply extend the TreeView and add a onMouseDragged event handler to it. like this
public ExtendedTreeView()
{
super();
setOnMouseDragged((MouseEvent event) -> onMouseMoved(event));
}
The event handler then looks like this (System outs are bascially just for debug purpose)
private void onMouseMoved(MouseEvent event)
{
System.out.println("onMouseMoved-----------------------------------------------------------------");
System.out.println(event.getEventType().toString());
// -----only apply when there is a drag event in progress
if(event.getEventType().equals(MouseEvent.MOUSE_DRAGGED))
{
// -----first check if we are the scrollEdges
System.out.println(String.format("Size: %f : %f", getWidth(), getHeight()));
System.out.println(String.format("Bounds: %f : %f : %f : %f", _scrollBounds.getTop(), _scrollBounds.getRight(), _scrollBounds.getBottom(), _scrollBounds.getLeft()));
System.out.println(String.format("Node: %f : %f", event.getX(), event.getY()));
//-----up and down directions are preferred
ScrollDirection direction = ScrollDirection.None;
if(event.getY() <= _scrollBounds.getTop())
direction = ScrollDirection.Top;
else if(event.getY() >= _scrollBounds.getBottom())
direction = ScrollDirection.Bottom;
else if(event.getX() >= _scrollBounds.getRight())
direction = ScrollDirection.Right;
else if(event.getX() <= _scrollBounds.getLeft())
direction = ScrollDirection.Left;
System.out.println(String.format("Direction: %s", direction.toString()));
}
}
When I drag and move the mouse in the TreeView it get the desired result.
The Problem is how ever
as soon as I drag an actuall Item, the drag event only occurs once and then never again.
The Item also has a drag and drop handling which essentially looks like this.
Note that this is actually a controller which has info about which TreeCell he belongs to.
#FXML
public void onDragDetected(MouseEvent event)
{
Base item = getTreeCell().getItem();
Dragboard dragboard = getTreeCell().startDragAndDrop(TransferMode.MOVE);
//-----get info from the item and put it in the clipboard
dragboard.setContent(content);
//event.consume();
}
#FXML
public void onDragEntered(DragEvent event)
{
boolean acceptDrag = false;
Dragboard dragboard = event.getDragboard();
Base current = getTreeCell().getItem();
//-----get the info from the clipboard and do something with it
//-----essentially the acceptDrag will be set to true here
if((_isDragAccepted = acceptDrag))
{
event.acceptTransferModes(TransferMode.MOVE);
//event.consume();
}
}
#FXML
public void onDragOver(DragEvent event)
{
if(_isDragAccepted)
{
event.acceptTransferModes(TransferMode.MOVE);
//event.consume();
}
}
#FXML
public void onDragExited(DragEvent event)
{
_isDragAccepted = false;
//event.consume();
}
#SuppressWarnings("unchecked")
#FXML
public void onDragDropped(DragEvent event)
{
Dragboard dragboard = event.getDragboard();
TreeItem<Base> currentItem = getTreeCell().getTreeItem();
Base current = getTreeCell().getItem();
//-----get info from clipboard again and do the necessary stuff
//-----essentially an item will be transfered here from one node to another
event.setDropCompleted(true);
//event.consume();
}
I also got some information about the event handling in javafx from this link
According to this if the event was not consumed, it should bubble up all the way to the source again, hence it should also pass the TreeView should it not ? So I would really like to know what I am doing wrong here.
Ok So I figured out what the problem was,
I was listening to the wrong event, the event I needed to register was the onDragOver event instead of the onMouseDragged event.
So in case anyone ever needs an autoscrollTreeView, the final solution for an autoscroll treeView looks like this now:
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.DragEvent;
import com.sun.javafx.scene.control.skin.VirtualFlow;
#SuppressWarnings("restriction")
public class AutoscrollTreeView<T> extends TreeView<T>
{
// region Enumerations
private enum ScrollDirection
{
None, Top, Bottom
}
// endregion
// region Static
private static final double _milliSecondToSecondFactor = 1.0d / 1000.0d;
// endregion
// region Fields
/**
* the time interval in milliseconds in which the scroll is performed
*/
private final LongProperty _checkInterval = new SimpleLongProperty(50);
/**
* the actual scroll speed when being in the scroll areas
*/
private final DoubleProperty _scrollSpeed = new SimpleDoubleProperty(1.0);
/**
* the scroll speed increment per second the user remain in the scroll area
*/
private final DoubleProperty _scrollSpeedIncrementPerSecond = new SimpleDoubleProperty(0.0);
/**
* distance from the top, which defines the area which will start a scroll in the -y axis
*/
private final DoubleProperty _dragIdentifierTop = new SimpleDoubleProperty();
/**
* distance from the bottom, which defines the area which will start a scroll in the +y axis
*/
private final DoubleProperty _dragIdentifierBottom = new SimpleDoubleProperty();
/**
* time at which the user entered the any scroll area
*/
private long _initialDragTime = -1;
/**
* last time the interval was checked
*/
private long _lastCheck = -1;
// endregion
// region Constructor
public AutoscrollTreeView()
{
super();
addEventHandlers();
}
public AutoscrollTreeView(TreeItem<T> root)
{
super(root);
addEventHandlers();
}
// endregion
// region Getter/Setter
public final void setCheckInterval(long value)
{
_checkInterval.set(value);
}
public final long getCheckInterval()
{
return _checkInterval.get();
}
public final LongProperty checkIntervalProperty()
{
return _checkInterval;
}
public final void setScrollSpeed(double value)
{
_scrollSpeed.set(value);
}
public final double getScrollSpeed()
{
return _scrollSpeed.get();
}
public final DoubleProperty scrollSpeedProperty()
{
return _scrollSpeed;
}
public final void setScrollSpeedIncrementPerSecond(double value)
{
_scrollSpeedIncrementPerSecond.set(value);
}
public final double getScrollSpeedIncrementPerSecond()
{
return _scrollSpeedIncrementPerSecond.get();
}
public final DoubleProperty scrollSpeedIncrementPerSecondProperty()
{
return _scrollSpeedIncrementPerSecond;
}
public final void setDragIdentiferTop(double value)
{
_dragIdentifierTop.set(value);
}
public final double getDragIdentifierTop()
{
return _dragIdentifierTop.get();
}
public final DoubleProperty dragIdentifierTopProperty()
{
return _dragIdentifierTop;
}
public final void setDragIdentiferBottom(double value)
{
_dragIdentifierBottom.set(value);
}
public final double getDragIdentifierBottom()
{
return _dragIdentifierBottom.get();
}
public final DoubleProperty dragIdentifierBottomProperty()
{
return _dragIdentifierBottom;
}
// endregion
// region Events
private void onDragEvent(DragEvent event)
{
// -----only apply when there is a drag event in progress
if(event.getEventType().equals(DragEvent.DRAG_OVER))
{
if(_lastCheck == -1 || System.currentTimeMillis() - _lastCheck > _checkInterval.get())
{
ScrollDirection direction = ScrollDirection.None;
if(event.getY() <= _dragIdentifierTop.get())
direction = ScrollDirection.Top;
else if(event.getY() >= getHeight() - _dragIdentifierBottom.get())
direction = ScrollDirection.Bottom;
if(direction != ScrollDirection.None)
{
double additionalScrollSpeed = 0;
if(_initialDragTime > 0)
additionalScrollSpeed = _scrollSpeedIncrementPerSecond.get() * (System.currentTimeMillis() - _initialDragTime) * _milliSecondToSecondFactor;
else
_initialDragTime = System.currentTimeMillis();
if(direction == ScrollDirection.Bottom)
scrollY(_scrollSpeed.get() + additionalScrollSpeed);
else
scrollY(-(_scrollSpeed.get() + additionalScrollSpeed));
}
else
{
_initialDragTime = -1;
}
_lastCheck = System.currentTimeMillis();
}
}
else
{
_initialDragTime = -1;
_lastCheck = -1;
}
}
// endregion
// region Private
/**
* adds the necessary event filters
*/
private void addEventHandlers()
{
addEventHandler(DragEvent.DRAG_OVER, event -> onDragEvent(event));
addEventHandler(DragEvent.DRAG_EXITED, event -> onDragEvent(event));
addEventHandler(DragEvent.DRAG_DROPPED, event -> onDragEvent(event));
addEventHandler(DragEvent.DRAG_DONE, event -> onDragEvent(event));
}
private void scrollY(double offset)
{
VirtualFlow<?> flow = ((VirtualFlow<?>) lookup("VirtualFlow"));
flow.adjustPixels(offset);
}
// endregion
}

java keylistener on linux

I'm trying to write a game in java3d on Linux and for that I need a proper KeyListener.
Did anyone of you know how to do it? I'm currently using following code, I found somewhere on the net. It's working pretty good, holding down just one key, but as soon, as I press more than one (like space and w) it will do unexpected things...
public class RepeatingReleasedEventsFixer implements AWTEventListener {
private final HashMap<Integer, ReleasedAction> _map = new HashMap<Integer, ReleasedAction>();
public void install() {
Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
}
public void remove() {
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
}
#Override
public void eventDispatched(AWTEvent event) {
assert event instanceof KeyEvent : "Shall only listen to KeyEvents, so no other events shall come here";
assert assertEDT(); // REMEMBER THAT THIS IS SINGLE THREADED, so no need for synch.
// ?: Is this one of our synthetic RELEASED events?
if (event instanceof Reposted) {
// -> Yes, so we shalln't process it again.
return;
}
// ?: KEY_TYPED event? (We're only interested in KEY_PRESSED and KEY_RELEASED).
if (event.getID() == KeyEvent.KEY_TYPED) {
// -> Yes, TYPED, don't process.
return;
}
final KeyEvent keyEvent = (KeyEvent) event;
// ?: Is this already consumed?
// (Note how events are passed on to all AWTEventListeners even though a previous one consumed it)
if (keyEvent.isConsumed()) {
return;
}
// ?: Is this RELEASED? (the problem we're trying to fix!)
if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
// -> Yes, so stick in wait
/**
* Really just wait until "immediately", as the point is that the subsequent PRESSED shall already have been
* posted on the event queue, and shall thus be the direct next event no matter which events are posted
* afterwards. The code with the ReleasedAction handles if the Timer thread actually fires the action due to
* lags, by cancelling the action itself upon the PRESSED.
*/
final Timer timer = new Timer(2, null);
ReleasedAction action = new ReleasedAction(keyEvent, timer);
timer.addActionListener(action);
timer.start();
_map.put(Integer.valueOf(keyEvent.getKeyCode()), action);
// Consume the original
keyEvent.consume();
}
else if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
// Remember that this is single threaded (EDT), so we can't have races.
ReleasedAction action = _map.remove(Integer.valueOf(keyEvent.getKeyCode()));
// ?: Do we have a corresponding RELEASED waiting?
if (action != null) {
// -> Yes, so dump it
action.cancel();
}
// System.out.println("PRESSED: [" + keyEvent + "]");
}
else {
throw new AssertionError("All IDs should be covered.");
}
}
/**
* The ActionListener that posts the RELEASED {#link RepostedKeyEvent} if the {#link Timer} times out (and hence the
* repeat-action was over).
*/
private class ReleasedAction implements ActionListener {
private final KeyEvent _originalKeyEvent;
private Timer _timer;
ReleasedAction(KeyEvent originalReleased, Timer timer) {
_timer = timer;
_originalKeyEvent = originalReleased;
}
void cancel() {
assert assertEDT();
_timer.stop();
_timer = null;
_map.remove(Integer.valueOf(_originalKeyEvent.getKeyCode()));
}
#Override
public void actionPerformed(#SuppressWarnings ("unused") ActionEvent e) {
assert assertEDT();
// ?: Are we already cancelled?
// (Judging by Timer and TimerQueue code, we can theoretically be raced to be posted onto EDT by TimerQueue,
// due to some lag, unfair scheduling)
if (_timer == null) {
// -> Yes, so don't post the new RELEASED event.
return;
}
// Stop Timer and clean.
cancel();
// Creating new KeyEvent (we've consumed the original).
KeyEvent newEvent = new RepostedKeyEvent((Component) _originalKeyEvent.getSource(),
_originalKeyEvent.getID(), _originalKeyEvent.getWhen(), _originalKeyEvent.getModifiers(),
_originalKeyEvent.getKeyCode(), _originalKeyEvent.getKeyChar(), _originalKeyEvent.getKeyLocation());
// Posting to EventQueue.
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(newEvent);
// System.out.println("Posted synthetic RELEASED [" + newEvent + "].");
}
}
/**
* Marker interface that denotes that the {#link KeyEvent} in question is reposted from some
* {#link AWTEventListener}, including this. It denotes that the event shall not be "hack processed" by this class
* again. (The problem is that it is not possible to state "inject this event from this point in the pipeline" - one
* have to inject it to the event queue directly, thus it will come through this {#link AWTEventListener} too.
*/
public interface Reposted {
// marker
}
/**
* Dead simple extension of {#link KeyEvent} that implements {#link Reposted}.
*/
public static class RepostedKeyEvent extends KeyEvent implements Reposted {
public RepostedKeyEvent(#SuppressWarnings ("hiding") Component source, #SuppressWarnings ("hiding") int id,
long when, int modifiers, int keyCode, char keyChar, int keyLocation) {
super(source, id, when, modifiers, keyCode, keyChar, keyLocation);
}
}
private static boolean assertEDT() {
if (!EventQueue.isDispatchThread()) {
throw new AssertionError("Not EDT, but [" + Thread.currentThread() + "].");
}
return true;
}
}
I can't be the only one who still runs into this - meanwhile 15 y.o. - problem and don't want to use timers...
EDIT: What this code is doing is fix the known problem on any Linux distri, where you add a simple KeyListener, which handles keyDowns, but invokes keyReleased Event repeatedly. To clearify my problem here a simple example
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class Test5 extends JFrame{
public Test5() {
addKeyListener(new KeyListener() {
boolean keydown = false;
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent arg0) {
keydown = false;
System.out.println("keyup");
}
#Override
public void keyPressed(KeyEvent arg0) {
if (keydown){
System.out.println("key is down");
} else {
System.out.println("key not down");
}
keydown = true;
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 400);
setVisible(true);
//new RepeatingReleasedEventsFixer().install(); // This line will fix it for one key pressed
}
public static void main(String[] args) {
new Test5();
}
}
The output without the line being commented out:
key not down
keyup
key not down
keyup
key not down
keyup
key not down
keyup
key not down
keyup
otherwise:
key not down
key is down
key is down
key is down
key is down
key is down
key is down
key is down
key is down
key is down
keyup
Btw. How come, that it's not beeing fixed by now?
EDIT:
I tried the KeyBindings, as suggested, where it comes to these problems:
public class Test5 extends JFrame{
long timestamp = 0;
public Test5() {
((JComponent)getComponent(0)).getInputMap().put(KeyStroke.getKeyStroke('a'), "a");
((JComponent)getComponent(0)).getActionMap().put("a", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("time: "+(System.currentTimeMillis()-timestamp));
timestamp = System.currentTimeMillis();
}
});
((JComponent)getComponent(0)).getInputMap().put(KeyStroke.getKeyStroke('s'), "s");
((JComponent)getComponent(0)).getActionMap().put("s", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("s");
}
});
((JComponent)getComponent(0)).getInputMap().put(KeyStroke.getKeyStroke('d'), "d");
((JComponent)getComponent(0)).getActionMap().put("d", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("d");
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 400);
setVisible(true);
new RepeatingReleasedEventsFixer().install(); // This line will fix it for one key pressed
}
/**
* #param args
*/
public static void main(String[] args) {
new Test5();
}
Holding down "a" will give me following output:
time: 4171
time: 501
time: 30
time: 30
time: 30
Where the second time is the actual problem. It takes about 470ms too long.
Holding down "s" and then somewhne pressing "d" will give me that output:
s
s
s
s
d
d
d
d
d
So I can't process two actions as the same time, so I can't use KeyBindings
This is not an answer, it is a long comment with a picture and some explanations.
I used your Test5 (without RepeatingReleasedEventsFixer) to hold down a and measure the time responses. The output is of the form
time: t1
time: t2
time: t3
time: t3
time: t3
...
t1 is meaningless since it depends on the current time and has nothing to do with response time (you also seem to ignore it).
t2 is the time it takes for the OS to realize that you're holding the key for repeated input.
t3 is the "sample time" of the held key, or a discretization of the input.
I'm using Windows where I have the following control panel options:
Repeat delay allows me to set t2 between ~257 (short) and ~1050 (long).
Repeat rate allows me to set t3 between ~407 (slow) and ~37 (fast).
For Linux, you'll have to consult someone / somewhere on how to change these values if you don't already know how to.
As for using multiple keys, see this question and answer and the excellent link within (especially the "Motion With Multiple Keys Pressed" section). It's a short tutorial and analysis of key bindings and key listeners, similar to the one I sent you to on this site.
Key bindings will always be preferred over key listeners unless maybe there is some very low level thing you want to do.
After days of researching and putting stuff together, I ended up writing my own Listener combined with a KeyEventDispatcher, here is the code for someone running into the same problem. It can and should be optimized, but is working for now:
Klass to test if a specific key is pressed:
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.HashMap;
public class IsKeyPressed {
private static boolean wPressed = false;
private HashMap<Integer, Boolean> keys = new HashMap<Integer, Boolean>();
public IsKeyPressed() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent ke) {
synchronized (IsKeyPressed.class) {
switch (ke.getID()) {
case KeyEvent.KEY_PRESSED:
keys.put(ke.getKeyCode(), true);
break;
case KeyEvent.KEY_RELEASED:
keys.put(ke.getKeyCode(), false);
break;
}
return false;
}
}
});
}
public static boolean isWPressed() {
synchronized (IsKeyPressed.class) {
return wPressed;
}
}
public boolean isPressed(int keyCode){
synchronized (IsKeyPressed.class) {
if (keys == null)
return false;
if (keys.get(keyCode) == null)
return false;
return keys.get(keyCode);
}
}
}
Abstract class, thats beeing used for the actions.
public abstract class KeyActionListener {
protected int keyCode;
public KeyActionListener(int keyCode) {
this.keyCode = keyCode;
}
public void setKeyCode(int keyCode){
this.keyCode = keyCode;
}
public int getKeyCode(){
return this.keyCode;
}
public abstract void onKeyDown();
public abstract void onKeyUp();
public abstract void onKeyHolding();
}
Start listening to the keys and run the actions.
import java.util.ArrayList;
import java.util.HashMap;
public class KeyThread extends Thread{
private int sleep = 3;
ArrayList<KeyActionListener> listener = new ArrayList<KeyActionListener>();
IsKeyPressed isPressed = new IsKeyPressed();
HashMap<KeyActionListener, Boolean> pressed = new HashMap<KeyActionListener, Boolean>();
public KeyThread() {
this.start();
}
public void run() {
while (true){
for (int i = 0; i < listener.size(); i++) {
KeyActionListener curListener = listener.get(i);
if (isPressed.isPressed(curListener.getKeyCode()) && !pressed.get(curListener)){
curListener.onKeyDown();
pressed.put(curListener, true);
} else if(!isPressed.isPressed(curListener.getKeyCode()) && pressed.get(curListener)) {
curListener.onKeyUp();
pressed.put(curListener, false);
}
if(isPressed.isPressed(curListener.getKeyCode())){
curListener.onKeyHolding();
}
try{
Thread.sleep(sleep);
} catch(InterruptedException e){
}
}
}
}
public void addKeyActionListener(KeyActionListener l){
listener.add(l);
pressed.put(l, false);
}
}

Java Multiple key presses and release

I've tried searching how to handle multiple key presses in java but all the solutions I've found are assuming that all keys are pressed.
I'm creating a very simple game where an image that I loaded will be moving along a canvas. i want it to move left, right, and jump by using the left, right, and up arrow keys. My problem is that, when I press the left and right keys, they work perfectly fine. But, when I press the up arrow key, it just jumps and stops moving all together.
I have an array list of all keys pressed and a run() method that checks all the keys pressed and does the specific job. But java only remembers the latest key that I pressed and it doesn't call the run() method once I press the up arrow key.
What do you suggest I do? I've tried threads but they mix up my code (or maybe I did something wrong?).
Here's my code:
public class Moving implements KeyListener {
ProgramClass bg; //this is another class
Set<Integer> pressed = new HashSet<Integer>();
public Moving (ProgramClass aa) {
bg = aa;
}
public void keyPressed (KeyEvent ae) {
pressed.add(ae.getKeyCode());
run();
}
public void keyTyped (KeyEvent ae) {
}
public void keyReleased (KeyEvent ae) {
pressed.remove (ae.getKeyCode());
if (bg.left==false) {
if (ae.getKeyCode()==KeyEvent.VK_UP) {
bg.yy -=40;
}
}
run();
}
public void run () {
if (bg.left==true) {
if (pressed.contains(KeyEvent.VK_A)) {}
if (pressed.contains(KeyEvent.VK_D)) {
if (bg.pic==true)
bg.pic = false;
else
bg.pic = true;
if (bg.x==-550)
bg.x = -2;
else
bg.x -= 2;
}
}
if (bg.left==false) {
if (pressed.contains(KeyEvent.VK_LEFT)) {
if (bg.pic==true)
bg.pic = false;
else
bg.pic = true;
if (bg.x==550)
bg.x = 2;
else
bg.x += 2;
}
if (pressed.contains(KeyEvent.VK_RIGHT)) {}
if (pressed.contains(KeyEvent.VK_UP)) {
bg.yy -=40;
}
}
}
}
Thanks!

Can i use JCheckbox to show "mixed state"

In windows it is possible to show a grayed out JCheckbox, to show that the collection of data which it represents not all items have the same value.
Is this even possible with a JCheckBox?
How do i go about this?
(Hoping there's a way to not override it)
Thanks
JIDE Common Layer has a TristateCheckBox.
It's possible with some of work.
I have this code from some years ago. Is based in some examples I found in internet, but I cannot find any reference to the original creator, so I apologize
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import java.awt.event.*;
/**
* Maintenance tip - There were some tricks to getting this code
* working:
*
* 1. You have to overwite addMouseListener() to do nothing
* 2. You have to add a mouse event on mousePressed by calling
* super.addMouseListener()
* 3. You have to replace the UIActionMap for the keyboard event
* "pressed" with your own one.
* 4. You have to remove the UIActionMap for the keyboard event
* "released".
* 5. You have to grab focus when the next state is entered,
* otherwise clicking on the component won't get the focus.
* 6. You have to make a TristateDecorator as a button model that
* wraps the original button model and does state management.
*/
public class TristateCheckBox extends JCheckBox {
/** This is a type-safe enumerated type */
public static class State { private State() { } }
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
private final TristateDecorator model;
public TristateCheckBox(String text, Icon icon, State initial){
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
grabFocus();
model.nextState();
}
});
map.put("released", null);
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public TristateCheckBox(String text, State initial) {
this(text, null, initial);
}
public TristateCheckBox(String text) {
this(text, DONT_CARE);
}
public TristateCheckBox() {
this(null);
}
/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l) { }
/**
* Set the new state to either SELECTED, NOT_SELECTED or
* DONT_CARE. If state == null, it is treated as DONT_CARE.
*/
public void setState(State state) { model.setState(state); }
/** Return the current state, which is determined by the
* selection status of the model. */
public State getState() { return model.getState(); }
public void setSelected(boolean b) {
if (b) {
setState(SELECTED);
} else {
setState(NOT_SELECTED);
}
}
/**
* Exactly which Design Pattern is this? Is it an Adapter,
* a Proxy or a Decorator? In this case, my vote lies with the
* Decorator, because we are extending functionality and
* "decorating" the original model with a more powerful model.
*/
private class TristateDecorator implements ButtonModel {
private final ButtonModel other;
private TristateDecorator(ButtonModel other) {
this.other = other;
}
private void setState(State state) {
if (state == NOT_SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state == SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(true);
} else { // either "null" or DONT_CARE
other.setArmed(true);
setPressed(true);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed
* state of the model.
*
* We return the SELECTED state when the checkbox is selected
* but not armed, DONT_CARE state when the checkbox is
* selected and armed (grey) and NOT_SELECTED when the
* checkbox is deselected.
*/
private State getState() {
if (isSelected() && !isArmed()) {
// normal black tick
return SELECTED;
} else if (isSelected() && isArmed()) {
// don't care grey tick
return DONT_CARE;
} else {
// normal deselected
return NOT_SELECTED;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/
private void nextState() {
State current = getState();
if (current == NOT_SELECTED) {
setState(SELECTED);
} else if (current == SELECTED) {
setState(DONT_CARE);
} else if (current == DONT_CARE) {
setState(NOT_SELECTED);
}
}
/** Filter: No one may change the armed status except us. */
public void setArmed(boolean b) {
}
/** We disable focusing on the component when it is not
* enabled. */
public void setEnabled(boolean b) {
setFocusable(b);
other.setEnabled(b);
}
/** All these methods simply delegate to the "other" model
* that is being decorated. */
public boolean isArmed() { return other.isArmed(); }
public boolean isSelected() { return other.isSelected(); }
public boolean isEnabled() { return other.isEnabled(); }
public boolean isPressed() { return other.isPressed(); }
public boolean isRollover() { return other.isRollover(); }
public void setSelected(boolean b) { other.setSelected(b); }
public void setPressed(boolean b) { other.setPressed(b); }
public void setRollover(boolean b) { other.setRollover(b); }
public void setMnemonic(int key) { other.setMnemonic(key); }
public int getMnemonic() { return other.getMnemonic(); }
public void setActionCommand(String s) {
other.setActionCommand(s);
}
public String getActionCommand() {
return other.getActionCommand();
}
public void setGroup(ButtonGroup group) {
other.setGroup(group);
}
public void addActionListener(ActionListener l) {
other.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
other.removeActionListener(l);
}
public void addItemListener(ItemListener l) {
other.addItemListener(l);
}
public void removeItemListener(ItemListener l) {
other.removeItemListener(l);
}
public void addChangeListener(ChangeListener l) {
other.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
other.removeChangeListener(l);
}
public Object[] getSelectedObjects() {
return other.getSelectedObjects();
}
}
}
My colleague who this question came from thought of this;
Create a dummy JCheckBox which is disabled and selected. set the same size as the real one.
Create an Icon which' paint method actually paints the dummy JCheckbox.
Set the original JCheckBox' Icon to the one painting the dummy.
Remove the icon as soon as the JCheckBox is clicked.
++ No overridden JCheckBox
-- not a real tri-state Combo
I think he's satisfied.
Thanks for the help

Categories