When i create the KeyListener, it requires the following fields:
public void keyPressed(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
When i put System.out.println(e) into the keyPressed method, though, it returns this when i press the enter key:
java.awt.event.KeyEvent[KEY_PRESSED,keyCode=10,keyText=?,keyChar=?,keyLocation=KEY_LOCATION_STANDARD,rawCode=0,primaryLevelUnicode=0,scancode=0] on javax.swing.JButton[,1,1,100x100,alignmentX=0.0,alignmentY=0.5,border=com.apple.laf.AquaButtonBorder$Dynamic#13b33a0e,flags=288,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=0,left=2,bottom=0,right=2],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=HI,defaultCapable=true]
This is obviously not a KeyEvent, so I cannot use it to call the keyPressed(KeyEvent e). What I want to be able to do is simulate the pressing of a key, specifically the enter key, in a way that would activate the keyListener and would output that text into a JTextArea.
Note: I looked at the accepted answer for How can I perfectly simulate KeyEvents?, and understood little of how it actually works, and i want code i understand. I also looked here How to simulate keyboard presses in java?, but not i could not get the robot to work; nothing happened when a key was supposed to be pressed.
e is the KeyEvent.
if you want to see the e value, then you can try this
System.out.println(e.getKeyChar());
Creating KeyEvent :
KeyEvent e = new KeyEvent(Component source, int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation);
Example (dunno if this is the right way, but it produce the right output):
Button a = new Button("click");
KeyEvent e;
e = new KeyEvent(a, 1, 20, 1, 10, 'a');
System.out.println(""+e.getKeyChar());
System.out.println(""+e.getKeyCode());
Here is the all type of KeyEvent parameters
java.awt.event.KeyEvent
#Deprecated public KeyEvent(Component source, int id, long when, int modifiers, int keyCode)
Deprecated. as of JDK1.1
===
java.awt.event.KeyEvent
public KeyEvent(Component source, int id, long when, int modifiers, int keyCode, char keyChar)
Constructs a KeyEvent object.
Note that passing in an invalid id results in unspecified behavior. This method throws an IllegalArgumentException if source is null.
Parameters:
source - the Component that originated the event id - an integer identifying the type of event when - a long integer that specifies the time the event occurred modifiers - the modifier keys down during event (shift, ctrl, alt, meta) Either extended _DOWN_MASK or old _MASK modifiers should be used, but both models should not be mixed in one event. Use of the extended modifiers is preferred. keyCode - the integer code for an actual key, or VK_UNDEFINED (for a key-typed event) keyChar - the Unicode character generated by this event, or CHAR_UNDEFINED (for key-pressed and key-released events which do not map to a valid Unicode character)
Throws:
IllegalArgumentException - if id is KEY_TYPED and keyChar is CHAR_UNDEFINED; or if id is KEY_TYPED and keyCode is not VK_UNDEFINED IllegalArgumentException - if source is null
===
java.awt.event.KeyEvent
public KeyEvent(Component source, int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation)
When using robot, first obtain focus of component to which you add your KeyListener to. Then you can use robot to simulte key presses. As an alternative, you can just use dispatchEvent on component to which your listener is added.
KeyEvent key = new KeyEvent(inputField, KeyEvent.KEY_TYPED, System.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, 'Z');
inputField.dispatchEvent(key);
Providing you have:
JInputField InputField = new JInputField();
You can as well create KeyEvent as described above and pass it to keyTyped method of your listener.
As for keyPrssed, you can do the same.
You state:
I believe it would make part of my code more efficient. When certain conditions are met (I am doing hang man, and this is a "cheat" that is a joke with my teacher) the computer will press the correct keys to "guess" the answer. and then there is the simple, i wonder if i can? part of it. that got started when i saw JButton.doClick() and wondered if it had one for JTextFields
As I suspected, you are going about this all wrong. If you want your program to press keys for you, there's no need to create KeyEvents. If the "keys" are JButtons, then simply calling doClick() on the button will do. If you are desiring to fill text into a JTextField, then simply setting the text is all that is needed. i.e.,
For instance if you called the bit of text below in a Swing Timer (to slow it down so that you see the text being added:
String myText = myTextField.getText();
myText += nextBitOfText;
myTextField.setText(myText);
You would likely get the effect you desire.
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class AddTextToTextField extends JPanel {
public static final String[] POSSIBLE_TEXTS = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
public static final int TIMER_DELAY = 500;
private JTextField myTextField = new JTextField(20);
private JButton myButton = new JButton(new BtnAction("Press Me"));
private Random random = new Random();
public AddTextToTextField() {
add(myTextField);
add(myButton);
}
private class BtnAction extends AbstractAction {
public BtnAction(String text) {
super(text);
}
#Override
public void actionPerformed(ActionEvent arg0) {
setEnabled(false);
myTextField.setText("");
myTextField.setFocusable(false);
String randomText = POSSIBLE_TEXTS[random.nextInt(POSSIBLE_TEXTS.length)];
new Timer(TIMER_DELAY, new TimerAction(this, randomText)).start();
}
}
private class TimerAction implements ActionListener {
private AbstractAction btnAction;
private String text;
private int count = 0;
public TimerAction(AbstractAction btnAction, String text) {
this.btnAction = btnAction;
this.text = text;
}
#Override
public void actionPerformed(ActionEvent e) {
if (count <= text.length()) {
myTextField.setText(text.substring(0, count));
count++;
} else {
((Timer)e.getSource()).stop();
btnAction.setEnabled(true);
myTextField.setFocusable(true);
}
}
}
private static void createAndShowGui() {
AddTextToTextField mainPanel = new AddTextToTextField();
JFrame frame = new JFrame("AddTextToTextField");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You can use this to dispatch a virtual event:
textArea.dispatchEvent(new KeyEvent(jFrame,
KeyEvent.KEY_TYPED, System.currentTimeMillis(),
0,
KeyEvent.VK_ENTER));
Try that out, it should work but that was from memory
Related
This is not a question for key events in specific. I use KeyEvents/KeyListeners and a JButton as an example. All I want is to be able to fire listeners in JUnit tests conviniently.
I am trying to fire a key listener in unit tests, but for some reason the listener is not fired.
public class Example {
public static void main(String[] args) {
AtomicBoolean keyReleasedRan = new AtomicBoolean(false);
JButton button = new JButton();
button.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
keyReleasedRan.set(true);
}
});
int modifiers = 100; //Something, we don't care
int keyCode = KeyEvent.VK_A; //something realistic
KeyEvent event = new KeyEvent(button, KeyEvent.KEY_RELEASED, System.currentTimeMillis() + 200,
modifiers, keyCode, 'a');
button.dispatchEvent(event);
System.out.println("KEY RELEASED RAN: " + keyReleasedRan.get());
}
}
I have tried multiple arguments in the event constructor in case it plays a role, but still the listener is never fired.
The only approach that it works but I really do not want to follow, is to iterate through KeyListener(s) and call keyReleased() method for each listener.
Any idea what am I doing wrong?
I tried another components instead of a JButton but same results.
As the comments said, if you attach it to a JFrame, it works.
As you're using it in a Unit Test, you also want to automatically close the JFrame.
So I've added frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
and frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));.
public class Test {
public static void main(String[] args) {
JButton button = new JButton("Run");
button.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
System.out.println("runs...");
}
});
JFrame frame = new JFrame();
frame.add(button);
frame.setSize(50, 50);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int modifiers = InputEvent.SHIFT_DOWN_MASK; //Something, we don't care
int keyCode = KeyEvent.VK_A; //something realistic
KeyEvent event = new KeyEvent(button, KeyEvent.KEY_RELEASED, System.currentTimeMillis() + 200,
modifiers, keyCode, 'a');
button.dispatchEvent(event);
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
}
}
This question already has answers here:
Keylistener not working for JPanel
(4 answers)
Closed 5 years ago.
My KeyListener isn't registering that any keys are being pressed, what can i do to fix this? i have tried using keycodes instead of the VK_DOWN format but that doesn''t make a difference. i am not using a jpanel..
import javax.swing.*;
import java.awt.event.*;
public class SnakeFrame extends JFrame
{
final int FRAME_WIDTH = 800;
final int FRAME_HEIGHT = 700;
private SnakeComponent scene;
class KeyPressListener implements KeyListener
{
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
switch( keyCode ) {
case KeyEvent.VK_UP:
// handle up
System.out.println("Hello");
case KeyEvent.VK_DOWN:
// handle down
scene.a("d");
case KeyEvent.VK_LEFT:
// handle left
scene.a("l");
case KeyEvent.VK_RIGHT :
// handle right
scene.a("r");
}
KeyListener key = new KeyPressListener();
scene.addKeyListener(key);
}
public void keyReleased(KeyEvent e){}
public void keyTyped(KeyEvent e) {}
}
class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
scene.moveSnake();
scene.eaten();
scene.gameOver();
}
}
public SnakeFrame()
{
scene = new SnakeComponent();
add(scene);
setSize(FRAME_WIDTH, FRAME_HEIGHT);
KeyListener key = new KeyPressListener();
scene.addKeyListener(key);
ActionListener listener = new TimerListener();
final int DELAY = 80;
Timer t = new Timer(DELAY, listener);
t.start();
}
}
I have tried if/else statements instead of the switch case and that doesn't work
KeyListener needs two things to work, it needs the component it is registered to to be focsuable AND have current keyboard focus, before it will generate keys, this is a known limitation to the KeyListeners
As with every other question on the subject, the recommended solution is to use the Key Bindings API which has been designed, amongst other things, to resolve this issue and give you control to determine at what focus level it should generate events
I'm trying to get the current caret position when the "<" character is typed, using a KeyboardFocusManager. Code below. If the text field is empty when they character is typed I would expect the caret position to be 0. However, the result I actually get is this: 0 0 1. Could anyone explain why this is happening?
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class TextEditor {
#SuppressWarnings("serial")
public static class TextClass extends JTextArea {
static int startpos = 0;
public boolean checkKeyTyped (KeyEvent e) {
String keystr = Character.toString(e.getKeyChar());
switch (keystr) {
case "<":
startpos = getSelectionStart();
System.out.print(" " + startpos);
}
return false;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
final JTextArea textArea = new TextClass();
frame.add(textArea);
frame.setVisible(true);
// Add keyboard listener
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
return ((TextClass) textArea).checkKeyTyped(e);
}
});
}
}
You are using a general Key Event dispatcher. The possible events are KEY_PRESSED, KEY_TYPED and KEY_RELEASED. Based on what you say, you need KEY_TYPED. So filter for that:
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.KEY_TYPED) {
return ((TextClass) textArea).checkKeyTyped(e);
}
}
});
It is not how you are supposed to do it, you are supposed to implement a KeyListener and add it to your JTextArea using addKeyListener(KeyListener), as next:
final JTextArea textArea = new TextClass();
...
textArea.addKeyListener(new KeyListener() {
#Override
public void keyTyped(final KeyEvent e) {
char key = e.getKeyChar();
switch (key) {
case '<':
System.out.print(" " + textArea.getSelectionStart());
}
}
#Override
public void keyPressed(final KeyEvent e) {
}
#Override
public void keyReleased(final KeyEvent e) {
}
});
Up to now, you get it printed 3 times because your method is called for each type of KeyEvent that is triggered whenever you type on a key:
KEY_TYPED
The "key typed" event. This event is generated when a character is
entered. In the simplest case, it is produced by a single key press.
Often, however, characters are produced by series of key presses, and
the mapping from key pressed events to key typed events may be
many-to-one or many-to-many.
KEY_PRESSED
The "key pressed" event. This event is generated when a key is pushed
down.
KEY_RELEASED
The "key released" event. This event is generated when a key is let
up.
So I have a GUI which acts kind of like a console. I want the user to enter text into a JTextField and press enter. I've used key bindings to make a callback for when the user presses enter.
Now I want to create a method called waitForInput() which waits for the user to enter something and returns it. What I'm trying is below. But it results in a java.lang.IllegalMonitorStateException when notify() is called in the callback function.
public class MainWindow{
private JFrame mainWindow;
private JTextArea textEntry;
private String inputStringMonitor = ""; // lock/user input value
private Boolean stringReady = false; //flag for wait while loop
public MainWindow(){
mainWindow = new JFrame("console");
textEntry = new JTextArea();
// set up key bindings
InputAction = new UserInputAction();
textEntry.getInputMap().put( KeyStroke.getKeyStroke( "ENTER" ),"EnterAction" );
textEntry.getActionMap().put( "EnterAction", InputAction);
//configure window
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setMinimumSize(new Dimension(800,675));
mainWindow.getContentPane().add(textEntry);
mainWindow.pack();
mainWindow.setVisible(true);
}
// callback action when user presses enter
public class UserInputAction extends AbstractAction
{
public void actionPerformed( ActionEvent bp )
{
System.out.println( "enter pressed" );
textEntry.setText("> ");
textEntry.setCaretPosition(2);
synchronized(inputStringMonitor){
stringReady = true;
inputStringMonitor = textEntry.getText();
inputStringMonitor.notify(); //causes exception
}
}
}
public String waitForInput() throws InterruptedException {
String retval = "";
synchronized(inputStringMonitor){
stringReady = false;
System.out.println("waiting");
while(!stringReady){
inputStringMonitor.wait();
}
retval = inputStringMonitor;
}
return retval;
}
}
I think that I have an idea of what you're trying to do, and if so, I feel that I have a better solution. Correct me if I'm wrong, but I think that you want to create a GUI text entry window that other programs can use, and that notifies other programs when text as been entered. If so, then a better solution is to use a tool that is already present within Swing GUI components -- PropertyChangeSupport. If you want to listen for changes in a String's state, then make the String a "bound" property, one that notifies the GUI if its state ever changes by firing the Swing innate property change method. This way outside classes can register as listeners and be notified of this change.
For instance, the class below extends JPanel, partly because this will give the class a SwingPropertyChangeSupport object as well as add/remove PropertyChangeListener methods, but if you don't want to extend the Swing component, you can easily roll your own by adding your own SwingPropertyChangeSupport object as well as add/remove PropertyChangeListener methods to your class.
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class CallBackGui extends JPanel {
// public constant for the propertyName
public static final String TEXT_ENTRY = "text entry";
private static final int ROWS = 20;
private static final int COLUMNS = 40;
private static final String CARET_MARKER = "> ";
private JTextArea textEntryArea = new JTextArea(ROWS, COLUMNS);
private String enteredText = ""; // "bound" property
public CallBackGui() {
textEntryArea.setText(CARET_MARKER);
textEntryArea.setWrapStyleWord(true);
textEntryArea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(textEntryArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
add(scrollPane);
int condition = WHEN_FOCUSED;
InputMap inputMap = textEntryArea.getInputMap(condition);
ActionMap actionMap = textEntryArea.getActionMap();
KeyStroke enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
inputMap.put(enterKeyStroke, TEXT_ENTRY);
actionMap.put(TEXT_ENTRY, new TextEntryAction());
}
public String getEnteredText() {
return enteredText;
}
// or can make this private if you wish it to not be changed by outside forces
public void setEnteredText(String enteredText) {
String oldValue = this.enteredText;
String newValue = enteredText;
this.enteredText = enteredText; // change our bound property here
// notify listeners here
firePropertyChange(TEXT_ENTRY, oldValue, newValue);
}
// used by Key Bindings
private class TextEntryAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
// call method to set bound poperty
setEnteredText(textEntryArea.getText().substring(CARET_MARKER.length()));
textEntryArea.setText(CARET_MARKER);
}
}
}
Then any outside class that has a reference to the displayed CallBackGui can register a property change listener onto this object and get notification. A very (overly) simple example:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class TestCallBackGui {
private static void createAndShowGui() {
CallBackGui callBackGui = new CallBackGui();
JFrame frame = new JFrame("CallBackGui");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(callBackGui);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// add our PropertyChangeListener
callBackGui.addPropertyChangeListener(CallBackGui.TEXT_ENTRY, new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Text Entered:");
// result held by newValue
System.out.println(evt.getNewValue());
// or can call callBackGui.getEnteredText()
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
The benefit -- avoidance of all low-level wait/notify/synchronized code, especially the use of this type of code on the Swing event thread, and instead use of safer higher level constructs. Also, since the Swing component actually uses a SwingPropertyChangeSupport object, all call backs will be made on the Swing event thread, an important point if the listening program is also a Swing GUI.
Okay. So here's a solution thanks to Titus
1) Using synchronized() in the callback blocks the EDT, so that's bad. Instead use the invokeLater() to notify asynchronously.
// thread to branch off in order to notify
Runnable doNotify = new Runnable() {
public void run() {
synchronized(inputStringMonitor){
userString = textEntry.getText();
inputStringMonitor.notify();
}
}
};
// callback function
public void actionPerformed( ActionEvent bp )
{
System.out.println( "enter pressed" );
textEntry.setText("> ");
textEntry.setCaretPosition(2);
SwingUtilities.invokeLater(doNotify);
}
2) Assigning to inputStringMonitor re-initializes the lock and messes things up. Instead use a dedicated lock, and a separate string for storing the actual data.
Basically, I have a dropdown menu containing templates. For example:
apple( )
banana( )
Once one of them is selected, it pastes onto a JTextArea. My problem is if "apple( )" is selected, I want "apple" and the two brackets non-deletable in the TextArea, and user can enter anything inside the brackets.
Can anyone give me any direction/ideas here? I have been searching on the internet and found very little about this.
Check out the Proctected Text Component. It allows you to mark individual pieces of text as protected so that it can't be changed or deleted.
It uses a DocumentFilter as well as a NavigationFilter.
For a simpler solution you might be able to just use a NavigationFilter. The example below shows how you can prevent the selection of text at the beginning of the Document. You should be able to customize it to also prevent selection of text at the end of the document as well.
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class NavigationFilterPrefixWithBackspace extends NavigationFilter
{
private int prefixLength;
private Action deletePrevious;
public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component)
{
this.prefixLength = prefixLength;
deletePrevious = component.getActionMap().get("delete-previous");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.setCaretPosition(prefixLength);
}
#Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.setDot(Math.max(dot, prefixLength), bias);
}
#Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.moveDot(Math.max(dot, prefixLength), bias);
}
class BackspaceAction extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e)
{
JTextComponent component = (JTextComponent)e.getSource();
if (component.getCaretPosition() > prefixLength)
{
deletePrevious.actionPerformed( null );
}
}
}
private static void createAndShowUI()
{
JTextField textField = new JTextField("Prefix_", 20);
textField.setNavigationFilter( new NavigationFilterPrefixWithBackspace(7, textField) );
JFrame frame = new JFrame("Navigation Filter Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(textField);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
You'll have to do this yourself. I'd suggest you to make an event handler that fires every time the text changes (Click here to find out how). And inside that handler check if the JTextArea still starts with "apple(" and ends with ")".