I have a clicklistener extended class which aims to cached any current actions of the actor during touchDown, and assigns it back when touchUp is triggered. However, it does not works for sequence or parallel actions.
public class MyClickListener extends ClickListener {
public Actor actor;
private final Array<Action> cachedActions = new Array<Action>();
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
super.touchUp(event, x, y, pointer, button);
actor = event.getListenerActor();
actor.addAction(btnScaleBackActions());
for(Action action:cachedActions)
{
//action.reset(); // i wants the actor to continue at where it stop
action.setTarget(actor);
action.setActor(actor);
actor.addAction(action);
}
cachedActions.clear();
}
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if(pointer==0)
{
actor = event.getListenerActor();
actor.setScale(0.9f);
cachedActions.addAll(actor.getActions());
actor.clearActions();
return super.touchDown(event, x, y, pointer, button);
}
else
{
return false;
}
}
My buttons testing:
// button touchUp continue its previous action at where it stop
btn1.addAction(Actions.scaleBy(1,1,3));
// button touchUp not continue it previous actions and complete stop
btn2.addAction(sequence(Actions.scaleBy(1,1,3)));
// button touchUp give nullException error
btn3.addAction(forever(Actions.scaleBy(1,1,3)));
//error :
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.scenes.scene2d.actions.RepeatAction.delegate(RepeatAction.java:29)
at com.badlogic.gdx.scenes.scene2d.actions.DelegateAction.act(DelegateAction.java:43)
Is it possible to continue sequence/parallel actions at where it stop at myClickListener class?
Here's an alternate idea. Rather than deal with removing and restoring actions, and subsequently dealing with the pools issue, you can wrap your actions in a new type of pausable action.
public class PausableAction extends DelegateAction {
public static PausableAction pausable(Action wrappedAction){
PausableAction action = Actions.action(PausableAction.class);
action.setAction(wrappedAction);
return action;
}
boolean paused = false;
public void pause (){
paused = true;
}
public void unpause (){
paused = false;
}
protected boolean delegate (float delta){
if (paused)
return false;
return action.act(delta);
}
public void restart () {
super.restart();
paused = false;
}
}
Now when getting your actions, wrap them in a pausable, for example:
btn1.addAction(PausableAction.pausable(Actions.scaleBy(1,1,3)));
And pause/unpause actions when you need to, like:
//...
actor = event.getListenerActor();
actor.setScale(0.9f);
for (Action action : actor.getActions())
if (action instanceof PausableAction)
((PausableAction)action).pause();
return super.touchDown(event, x, y, pointer, button);
The default behavior of actions that came from a pool (like from the Actions class) is to restart themselves when they are removed from an actor. It's actually not safe for you to be reusing these instances because they have also been returned to the pool and might get attached to some other actor unexpectedly.
So before you remove them from your actor, you need to set their pools to null.
private static void clearPools (Array<Action> actions){
for (Action action : actions){
action.setPool(null);
if (action instanceof ParallelAction) //SequenceActions are also ParallelActions
clearPools(((ParallelAction)action).getActions());
else if (action instanceof DelegateAction)
((DelegateAction)action).getAction().setPool(null);
}
}
//And right before actor.clearActions();
clearPools(actor.getActions());
Then, when you add them back to the actor, you'll want to add their pools back so they can go back to the Actions pools and be reused later to avoid GC churn.
private static void assignPools (Array<Action> actions){
for (Action action : actions){
action.setPool(Pools.get(action.getClass()));
if (action instanceof ParallelAction)
assignPools(((ParallelAction)action).getActions());
else if (action instanceof DelegateAction){
Action innerAction = ((DelegateAction)action).getAction();
innerAction.setPool(Pools.get(innerAction.getClass()));
}
}
}
//And call it on your actor right after adding the actions back:
assignPools(actor.getActions);
Related
In some cases a need my JButton to appear as if it were pressed. This depends on some boolean.
I tried to create my own ButtonModel that overrides the default isPressed() method but in that way the button appears pressed only in case the mouse pointer is on top of it (without pressing a mouse button). I need it to appear pressed also if the mouse is somewhere else.
So far I tried this:
class MyButtonModel extends DefaultButtonModel
{
private boolean appearPressed;
#Override
public boolean isPressed()
{
return super.isPressed() || appearPressed;
}
}
I cannot use a JToggleButton or something similar.
My button derives from another class that implements some additional features and derives itself from JButton.
UPDATE:
I'm running on Windows 10 and use the WindowsClassic Look&Feel.
you also need to override isArmed():
class MyButtonModel extends DefaultButtonModel
{
private boolean appearPressed = true;
#Override
public boolean isPressed()
{
return super.isPressed() || appearPressed;
}
#Override
public boolean isArmed() {
return super.isArmed() || appearPressed;
}
}
The partial reply was given by #Philipp Li and #camickr. Thank you.
My button model needs to override isArmed() in order to obtain the desired result.
However, once the button is pressed, it doesn't respond anymore.
Digging in the source of DefaultButtonModel, I found this:
public void setPressed(boolean b)
{
if ((isPressed() == b) || (!isEnabled()))
return;
...
}
This method is invoked by AbstractButton.doClick() in order to notify the various ActionListeners.
In other words, if the overridden method isPressed() returns true because I want to make the button appear pressed, a successive click on that button will be ignored.
A similar thing occurs within DefaultButtonModel.setArmed().
Therefore, I implemented my button model as follows:
class MyButtonModel extends DefaultButtonModel
{
boolean appearPressed = false;
private boolean withinSetPressedMethod = false;
private boolean withinSetArmedMethod = false;
#Override
public void setPressed(boolean pressed)
{
withinSetPressedMethod = true;
super.setPressed(pressed);
withinSetPressedMethod = false;
}
#Override
public void setArmed(boolean armed)
{
withinSetArmedMethod = true;
super.setArmed(armed);
withinSetArmedMethod = false;
}
#Override
public boolean isPressed()
{
if (withinSetPressedMethod)
return (super.isPressed());
else
return (super.isPressed() || appearPressed);
}
#Override
public boolean isArmed()
{
if (withinSetArmedMethod)
return (super.isArmed());
else
return (super.isArmed() || appearPressed);
}
} // class MyButtonModel
When i click SelectBox first time and type some key, then my method CreateAutoComplete is execute one time. When i unfocus SelectBox and click again and type some key, then method execute two times. Next three times... four...
Of course i want only one time everytime.
private SelectBox<String> sbNationality;
private AutoComplete auto = new AutoComplete();
...
sbNationality.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
addListener(new ClickListener() {
#Override
public boolean keyTyped(InputEvent event, char character) {
auto.CreateAutoComplete(sbNationality, character);
return super.keyTyped(event, character);
}
});
super.clicked(event, x, y);
}
});
Your listener is registering a new listener at each click event, this is why you get duplicate actions.
It sems that the ClickListener you use, has a keyTyped method which is what interests you (the key typed event), not the click event by itself, so try this :
addListener(new ClickListener() {
#Override
public boolean keyTyped(InputEvent event, char character) {
auto.CreateAutoComplete(sbNationality, character);
return super.keyTyped(event, character);
}
});
Here's a simple example, most likely not meeting your requirements, but should give you the general idea.
// Add this field as a flag for you to know whether the sbNationality has been clicked or not.
private Boolean isSbNationalityClicked = false;
// On click, set the flag to true
sbNationality.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y){
isSbNationalityClicked = true;
super.clicked(event, x, y);
}
});
addListener(new ClickListsner(){
#Override
public boolean keyTyped(InputEvent, char characer){
// if you caught the keyTyped event and the flag is true - perform your desired action
if(isSbNationalityClicked){
auto.CreateAutoComplete(sbNationality, character);
// set flag to false, since the desired action has been executed
// you might want to set flag to false in some other cases as well,
// like mouseReleased or mouseLeave
isSbNationalityClicked = false;
return super.keyTyped(event, character);
}
}
});
Using the code above, once your sbNationality gets clicked, it'll set the flag to true, so no matter what you do meanwhile, keyTyped listener will act like it was clicked. You might wanna catch some other events like mouseReleased or mouseLeave to handle the value of isSbNationalityClicked flag.
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);
}
}
I set up a stage with three TextFields in my libgdx app, and I get different behaviour in the desktop mode and the Android mode. On Android, typing the enter key moves the cursor to the next TextField. On the desktop, typing the enter key does nothing.
How can I make the cursor move consistently on both platforms? I want to be able to set the focus to another field when the user types enter. On Android, whatever I set the focus to, the default enter key behaviour then jumps the focus to the field after that.
Here's the code I'm currently using to move the cursor and clear the next field:
stage.addListener(new InputListener() {
#Override
public boolean keyUp(InputEvent event, int keycode) {
if (keycode == Input.Keys.ENTER) {
nextField();
}
return false;
}
});
Gdx.input.setInputProcessor(stage);
}
private void nextField() {
TextField nextField =
stage.getKeyboardFocus() == text1
? text2
: stage.getKeyboardFocus() == text2
? text3
: text1;
nextField.setText("");
stage.setKeyboardFocus(nextField);
}
I've tried cancelling the event or returning true from the handler methods, but the focus still moves after my code finishes.
My complete sample code is on GitHub.
TextField uses a private internal InputListener, that gets initialized in the constructor and cannot be easily overwritten. The relevant code that changes the focus is during the keyTyped method of this listener:
public boolean keyTyped (InputEvent event, char character) {
[...]
if ((character == TAB || character == ENTER_ANDROID) && focusTraversal)
next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
[...]
}
One easy solution would be to disable focus traversals all together and set a com.badlogic.gdx.scenes.scene2d.ui.TextFieldListener that automatically does the traversals instead:
TextField textField
textField.setFocusTraversal(false);
textField.setTextFieldListener(new TextFieldListener() {
#Override
public void keyTyped(TextField textField, char key) {
if ((key == '\r' || key == '\n')){
textField.next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
}
}
});
If you need to be able to enable and disable focus traversals using TextFields setFocusTraversal method, there would also be a quite hacky solution by wrapping the internal InputListener inside your own listener when it is added to the TextField (but I would not recommend this):
class MyTextField extends TextField{
class InputWrapper extends InputListener{
private final InputListener l;
public InputWrapper(InputListener l) {
super();
this.l = l;
}
#Override
public boolean handle(Event e) {
return l.handle(e);
}
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
return l.touchDown(event, x, y, pointer, button);
}
#Override
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
l.touchUp(event, x, y, pointer, button);
}
#Override
public void touchDragged(InputEvent event, float x, float y,
int pointer) {
l.touchDragged(event, x, y, pointer);
}
#Override
public boolean mouseMoved(InputEvent event, float x, float y) {
return l.mouseMoved(event, x, y);
}
#Override
public void enter(InputEvent event, float x, float y, int pointer,
Actor fromActor) {
l.enter(event, x, y, pointer, fromActor);
}
#Override
public void exit(InputEvent event, float x, float y, int pointer,
Actor toActor) {
l.exit(event, x, y, pointer, toActor);
}
#Override
public boolean scrolled(InputEvent event, float x, float y,
int amount) {
return l.scrolled(event, x, y, amount);
}
#Override
public boolean keyDown(InputEvent event, int keycode) {
return l.keyDown(event, keycode);
}
#Override
public boolean keyUp(InputEvent event, int keycode) {
return l.keyUp(event, keycode);
}
#Override
public boolean keyTyped(InputEvent event, char character) {
if (isDisabled()) {
return false;
} else if ((character == '\r' || character == '\n')){
next(Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT));
return true;
}
return l.keyTyped(event, character);
}
}
public MyTextField(String text, Skin skin, String styleName) {
super(text, skin, styleName);
}
public MyTextField(String text, Skin skin) {
super(text, skin);
}
public MyTextField(String text, TextFieldStyle style) {
super(text, style);
}
boolean initialized = false;
#Override
public boolean addListener (EventListener l) {
if (!initialized) {
if (!(l instanceof InputListener)) {
throw new IllegalStateException();
}
initialized = true;
return super.addListener(new InputWrapper((InputListener) l));
}
return super.addListener(l);
}
}
edit:
On a second thought, you could also do this with the first solution by simply overwriting setFocusTraversal of the TextField and enabling/disabling your own listener during calls to this method.
I found a workaround, but I'd still appreciate a cleaner solution that gets both platforms to behave the same way.
I added a flag to indicate whether the focus will move by default, and I only change the focus if it won't move on its own. I then set that flag from the MainActivity class for Android or the Main class for desktop. I've posted the complete sample code on GitHub.
private void nextField() {
TextField nextField =
stage.getKeyboardFocus() == text1
? text2
: stage.getKeyboardFocus() == text2
? text3
: text1;
nextField.setText("");
if ( ! isFocusMovedAutomatically) {
stage.setKeyboardFocus(nextField);
}
}
In gdx-1.9.4 I was able to do the following:
final TextField newMessageTextField = new TextField("", uiSkin){
#Override
protected InputListener createInputListener () {
return new TextFieldClickListener(){
#Override
public boolean keyUp(com.badlogic.gdx.scenes.scene2d.InputEvent event, int keycode) {
System.out.println("event="+event+" key="+keycode);
return super.keyUp(event, keycode);
};
};
}
};
After pressing the arrows with focus on the TextField I've got
event=keyUp key=19
event=keyUp key=20
event=keyUp key=22
event=keyUp key=21
I'm trying to get when the mouse just clicked, not when the mouse is pressed.
I mean I use a code in a loop and if I detect if the mouse is pressed the code will execute a lot of time, but I want execute the code only Once, when the mouse has just clicked.
This is my code :
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
//Some stuff
}
See http://code.google.com/p/libgdx/wiki/InputEvent - you need to handle input events instead of polling, by extending InputProcessor and passing your custom input processor to Gdx.input.setInputProcessor().
EDIT:
public class MyInputProcessor implements InputProcessor {
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
// Some stuff
return true;
}
return false;
}
}
And wherever you want to use that:
MyInputProcessor inputProcessor = new MyInputProcessor();
Gdx.input.setInputProcessor(inputProcessor);
If find it easier to use this pattern:
class AwesomeGameClass {
public void init() {
Gdx.input.setInputProcessor(new InputProcessor() {
#Override
public boolean TouchDown(int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
onMouseDown();
return true;
}
return false
}
... the other implementations for InputProcessor go here, if you're using Eclipse or Intellij they'll add them in automatically ...
});
}
private void onMouseDown() {
}
}
You can use Gdx.input.justTouched(), which is true in the first frame where the mouse is clicked. Or, as the other answer states, you can use an InputProcessor (or InputAdapter) and handle the touchDown event:
Gdx.input.setInputProcessor(new InputAdapter() {
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (button == Buttons.LEFT) {
// do something
}
}
});
Without InputProcessor you can use easy like this in your render loop:
#Override
public void render(float delta) {
if(Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)){
//TODO:
}
}