Inconsistency calling TextField text in setOnKeyTyped - java

I am making a typing test in Java with JavaFX. I want to compare the text that is being typed in a TextField to the defined random words. However, the TextField only updates with the letter just typed sometimes and not all the times. The following code is where this problem is occurring.
field.setOnKeyTyped(e -> {
String typingWord = field.getText();
if( typingWord.isEmpty() &&
e.getCharacter().charAt(0) == '\b' &&
!(typedWords.size() == 0) &&
typingWord.equals(previousWord)){
field.setText(typedWords.get(typedWords.size() - 1));
typedWords.remove(typedWords.size() - 1);
field.positionCaret(typingWord.length());
index--;
}
//compare random words to typed words
if (Character.isWhitespace(e.getCharacter().charAt(0))) {
typedWords.set(index, typingWord);
field.clear();
e.consume();
index++;
}
previousWord = field.getText();
});
Is this purely due to the speed of my computer or is it just a bug in JavaFX?

Sir i do not know what you want to do but i want to help, so in your onKeyPressed() or onKeyReleased(), it gives a KeyEvent that is e in your case, you can use that in place of Character.isWhitespace() & e.getCharacter().charAt(0) and also /b
To get your KeyCode
field.setOnKeyTyped(e -> {
KeyCode kc = e.getCode(); // your keycode here is the character you just pressed
now with the KeyCode you can check for a whole lot of goodies,without falling to character
equality checks
if(kc == KeyCode.BACK_SPACE) //checking for your backspace
if(kc.isWhitespaceKey()) // your enter, tabs, space etc
also you can make use of these methods
TextField.deletePreviousChar();,TextField.deleteNextChar(); that might save you all the selection before clearing. if you want to clear like 10 of them then loop it 10 times
lastly why do you check for emptiness & later check if its not empty

Related

My else statement on my comboBox state changed event is running while conditions are not met...Java

need some help here with my code.
I have a combobox which in the initialization stage, its selectedIndex is set to -1 (empty).
In my state changed listener for the combo box i have the code i will paste below.
What its supposed to do is check if the selected index of the combobox is -1 then do nothing in my if statement, but if the index is not -1 it will print a row on my jTable with some information on it (this is my else statement) based on the selected index and then set the index back to -1 to deselect any items on my comboBox.
It prints the information on m jTable just fine, but the problem is my else statement seems to run even when the selected index for my combobox is -1.
This problem happens the first time i run the program. there is no other code which prints information on my jTable besides this code. please help
private void jComboBoxItemNameItemStateChanged(java.awt.event.ItemEvent evt) {
if(evt.getStateChange() == ItemEvent.SELECTED) {
if(jComboBoxItemName.getSelectedIndex() == -1){
System.out.println("nothing");
}
else {
try {
addResultOnTable();
jComboBoxItemName.setSelectedIndex(-1);
//displaySearchOnTable();
}
catch (Exception ex) {
System.out.println("comboBox is empty");
Logger.getLogger(Inventory.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
The only way to set the selected index of the JComboBox to -1 (minus one) is through code and not through a user action. When you set the JComboBox index to -1 in the code (as you do in the jComboBoxItemNameItemStateChanged method), the JComboBox fires an ItemEvent but it is always a DESELECTED state change and never a SELECTED state change. Hence your inner if statement will never be true. I am referring to this line of the code in your question:
if(jComboBoxItemName.getSelectedIndex() == -1 && firstRun == true){
By the way, since you always set firstRun to true in method jComboBoxItemNameItemStateChanged, the && firstRun == true will always be true. Besides which, people usually just write firstRun. Since firstRun is a boolean, there is no need for the == true.
All you need to do (in the jComboBoxItemNameItemStateChanged method) is print the value of the state change and the JComboBox selected index to verify this.
private void jComboBoxItemNameItemStateChanged(ItemEvent evt) {
String state = switch (evt.getStateChange()) {
case ItemEvent.DESELECTED -> "De-selected";
case ItemEvent.SELECTED -> "Selected";
default -> "Unknown";
};
System.out.print("State change: " + state);
JComboBox<?> combo = (JComboBox<?>) evt.getSource();
int ndx = combo.getSelectedIndex();
System.out.println(" , index = " + ndx);
}
Note that the JComboBox index is always set before the above code is executed. The above code also verifies this. So the following statement, in your question, is not true.
my else statement seems to run even when the selected index for my combobox is -1
Whenever the following condition (also from your code) is true, the JComboBox selected index cannot be -1 (minus one).
evt.getStateChange() == ItemEvent.SELECTED
Also note that the above code uses switch expressions which were added since Java 12.
I don't mean to be rude or condescending, but my code, above, is one way to debug
your code. Every programmer needs to learn how to debug their code.

How do I make the event triggered by a key only happen once?

Disclaimer: I'm really new at this and I apologize in advance if:
1) my question has already been asked (I've tried searching and had a lot of trouble finding what I needed)
or 2) if I'm not asking the question correctly.
Basically, I'm trying to make a game where pressing the spacebar triggers a sort of "super-power" that will perform a set of actions just once. Afterwards, if they try to press it again, it'll run up some sort of dialogue box that says their one-time super-power has already been used.
What I have:
try {
Key move = canvas.getLastKey();
int space = 0;
if(move == Key.SPACE) {
if (space == 0) {
space = 1;
}
if (space == 2){
JOptionPane.showMessageDialog(null, "superpower already used");
}
}
if( space == 1 ) {
//action here
canvas.resetKey();
space = 2;
}
}
Right now, the super-hero action is on an endless loop but if I reset the key here:
if(move == Key.SPACE) {
if (space == 0) {
space = 1;
canvas.resetKey();
}
the user can just use the super-power over and over again. Any help would be appreciated!
In the third line, you have written int space=0 so your variable is constantly reset to 0...
You have to initialize it elsewhere (the beginning of your program is a good place for any global variable).
You should consider moving int space = 0, outside of the try block. I suppose your try block gets invoked repeatedly, so you should declare this variable under a global scope.

JavaFX stop KeyEvent after getting only one alpha numeric character

I am new to Java and am having trouble on getting some code working. I have a GUI with the letters A-Z. After a button click, I want to ask for a Keyevent to do a couple of things:
Only allow you to select a letter. Upper or lower case. If not wait for a response that is correct.
Then check the alphabet list to see if it has been pressed before. If it has then ask for another letter
If a new character is entered then strike it out on the alphabet and run the method that follows
Disable any further keys being pressed.
I have tried the following code:
private static void spinGame(){
switch (wheelResult)
{
case "1'
...
break;
case "2":
...
break;
default:
System.out.println("...");
newPhrase.gameB();
scene2.setOnKeyPressed((KeyEvent event) -> {
if (event.getText().isEmpty())
return;
char pressed = event.getText().toUpperCase().charAt(0);
userGuess=event.getText().charAt(0);
if ((pressed < 'A' || pressed > 'Z'))
return;
Text t = alphabet.get(pressed);
if (t.isStrikethrough())
return;
// mark the letter 'used'
else{t.setFill(Color.BLUE);
t.setStrikethrough(true);
System.out.println(userGuess);
}
int letterCount;
if ((userGuess == 'a') || (userGuess == 'e') || (userGuess == 'i') || (userGuess == 'o') || (userGuess == 'u')){
playerScores[currentPlayer] -= 250;
System.out.println("£250 docked from score for vowel use");
}
It goes wrong from here. I dont want keys to be pressed again and I don't the following method should run:
letterCount = newPhrase.makeGuess(userGuess);
...my method....;
})
I have no idea how to fix it. I tested and recorded the User guess is being selected but it does not proceed the method after and the key in input doesn't stop. I also feel that my coding for the alphanumeric stuff is wrong.
Your solution already caters for your requirements as you check for a valid letter and whether or not it is strike-through in your alphabet. So until they provide an unused letter, nothing will happen aside from the conditions being checked each time
To stop the key input I'd recommend using a ToggleButton or something similar so the user has to indicate when they want to guess. This means you can use other text based controls with a decreased probability of striking off letters in your alphabet accidentally. When they guess an unused letter you can reset the button, otherwise keep checking their input for a valid letter
Your method relating to the scoring system is only called when a vowel has been entered by the user. You can separate the score and input logic by creating a method and only providing valid unused letters to it, handling the game's scoring/phrase lookup logic there:
private void checkPhrase(char chosenLetter){
//Handle the Hangman-like logic here
boolean isVowel = vowels.indexOf(chosenLetter) >= 0;
if (isVowel) {
System.out.println("User provided a vowel");
}
}
This will make it easier to read and maintain your code as once you've determined that the letter hasn't been used you will only need to call it once. Whereas before you might have had duplication as it looks like it was only been executed for vowels
Below are the changes I made to your code when testing in case they are of use:
public class AlphabetGuess extends Application {
private String vowels = "AEIOU";
#Override
public void start(Stage primaryStage) throws Exception {
ObservableList<Text> alphabet = FXCollections.observableArrayList();
for(char letter = 'A'; letter <= 'Z'; letter++){
alphabet.add(new Text(String.valueOf(letter)));
}
HBox alphabetContainer = new HBox(5);
alphabetContainer.setAlignment(Pos.CENTER);
alphabetContainer.getChildren().addAll(alphabet);
ToggleButton button = new ToggleButton("Listen for guess");
button.setSelected(false);
VBox root = new VBox(20, alphabetContainer, button);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 500, 100);
scene.setOnKeyPressed(event -> {
//If the button isn't selected, don't process the input
if (event.getText().isEmpty() || !button.isSelected()) {
return;
}
char userGuess = event.getText().toUpperCase().charAt(0);
if ((userGuess < 'A' || userGuess > 'Z')) {
return;
}
//This will cause letters A-Z to become 0-25 allowing the list to be indexed
Text text = alphabet.get(userGuess - 'A');
if (text.isStrikethrough()) {
System.out.println(userGuess + " has already been guessed");
return;
} else {
text.setFill(Color.BLUE);
text.setStrikethrough(true);
checkPhrase(userGuess);
}
//Un-select button which will cause this event to reject incoming keys
button.setSelected(false);
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void checkPhrase(char chosenLetter){
//Handle the Hangman-like logic here
boolean isVowel = vowels.indexOf(chosenLetter) >= 0;
if (isVowel) {
System.out.println("User provided a vowel");
}
//ToDo: Implement
}
}

Testing if # key is pressed in Java

for a program that I am writing I want to test if a few keys are pressed and then determine what to do out the combination of keys. so I got some problems trying to get the # key to be registered (Shift + 2) I tried this If statement
if (ke.getKeyCode() == KeyEvent.VK_2 && ke.getKeyCode() == KeyEvent.VK_SHIFT) {
Why does it not work? I am testing for the 2 and Shift key to be activated at the same time, o does it not work like that?
Use KeyEvent.VK_AT instead of looking for SHIFT + 2. The # symbol isn't in the same place on all keyboards.
So your code becomes:
if (ke.getKeyCode() == KeyEvent.VK_AT) {
}
If you really want to check if the shift key is being pressed have a look at InputEvent.getModifiers() or InputEvent.isShiftDown()
Based on updates spec in the comments, what you want to do is:
if (ke.getKeyChar() == '#') {
}

How to handle/decide the Text Field which a Key Char is being typed into upon its speed between KEY_PRESSED and KEY_RELEASED?

My problem is explained in the following scenario
There are 2 JTextField objects. Let's give them variable names as jTextField1 and jTextField2. And the current focus owner is jTextField2. And the User and/or a Device is firing key inputs into the program. But according to the speed of each key being pressed and released the program should decide which text field the key char of that key input should be entered into. For an example, if the difference between KEY_PRESSED and KEY_RELEASED is less than or equal to 50 milliseconds, the Key Char should be typed into jTextField1 and never into jTextField2 but while the Focus is still owned by jTextField2 and unchanged. If the difference between the events are greater than 50 milliseconds, the text will be typed into its current Focus Owner which is jTextField2 (Or it may be any other object too according to what the User has the Focus at the moment.). So if there were 2 simultaneous activities happening, for an example a user is typing keys in a speed of greater than 50 milliseconds per key typing, and another device like a Barcode Scanner is firing Key Events in a speed less than 50 milliseconds, both of these inputs should be entered or typed into those different text fields separately according to their typing speeds while the focus owner is always at the object which the user is interacting with.
Here is the current code I have written which works well but with the problem that when keys are pressed in a speed less than 50 milliseconds per key, it sets at jTextField1 but it also gets typed at jTextField2.
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
millis = System.currentTimeMillis();
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
enter1 = true;
}
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
if ((System.currentTimeMillis() - millis) <= 10) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
enter2 = true;
} else {
barcodeStringBuilder.append(e.getKeyChar());
}
if (enter1 && enter2) {
compo = getFocusOwner();
jTextField1.setText(barcodePool.toString());
barcodeStringBuilder.setLength(0);
compo.requestFocus();
}
enter1 = false;
enter2 = false;
} else {
}
} else if (e.getID() == KeyEvent.KEY_TYPED) {
}
return false;
}
The previous attempt I made to accomplish this task was trying to handle the input of the two devices which are a Keyboard and a Barcode Scanner separately using the Java HID API which also was unsuccessful. You can find my question thread on it here. And then I made it into this option which so far seems to be a good option.
So, does anyone know an effective way to accomplish my task as I have described above?
Thank you! And your help is highly appreciated.
I would not give either JTextFields focus, would not use a KeyEventDispatcher, but would instead try to use Key Bindings, and then based on the timing, append the text into the appropriate text field. This would prevent focus issues sending the text to two text fields.

Categories