我遇到了在JavaFX中检测单键按下的问题。我必须检测箭头键,但每次按下任何一个键时,我的代码的一部分被多次调用。我意识到这是因为AnimationTimer()
是一个循环因此这是原因,但我不知道如何检测单键敲击。无论如何,这是代码:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.HashSet;
public class Main extends Application {
Stage window;
static Scene scene;
private static char[][] mapa = {
{'X','X','X','X','X'},
{'X','.','.','.','X'},
{'X','.','M','.','X'},
{'X','.','.','.','X'},
{'X','X','X','X','X'}
};
private final int sizeX = 16;
private final int sizeY = 16;
static HashSet<String> currentlyActiveKeys;
@Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("Hello World");
Group root = new Group();
scene = new Scene(root);
window.setScene(scene);
Canvas canvas = new Canvas(512 - 64, 256);
root.getChildren().add(canvas);
prepareActionHandlers();
GraphicsContext gc = canvas.getGraphicsContext2D();
new AnimationTimer() {
@Override
public void handle(long now) {
gc.clearRect(0,0,512,512);
for(int i = 0; i < mapa.length; i++) {
for(int j = 0; j < mapa[i].length; j++) {
if(mapa[i][j] == 'X') {
gc.setLineWidth(5);
gc.setFill(Color.RED);
gc.fillRect(sizeX + i*sizeX, sizeY + j*sizeY, sizeX, sizeY);
} else if(mapa[i][j] == '.') {
gc.setLineWidth(5);
gc.setFill(Color.GREEN);
gc.fillRect(sizeX + i*sizeX, sizeY + j*sizeY, sizeX, sizeY);
} else if(mapa[i][j] == 'M') {
gc.setLineWidth(5);
gc.setFill(Color.BLACK);
gc.fillRect(sizeX + i*sizeX, sizeY + j*sizeY, sizeX, sizeY);
}
}
}
if(currentlyActiveKeys.contains("LEFT")) {
System.out.println("left");
}
if(currentlyActiveKeys.contains("RIGHT")) {
System.out.println("right");
}
if(currentlyActiveKeys.contains("UP")) {
System.out.println("up");
}
if(currentlyActiveKeys.contains("DOWN")) {
System.out.println("down");
}
}
}.start();
window.show();
}
private static void prepareActionHandlers()
{
currentlyActiveKeys = new HashSet<String>();
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event)
{
currentlyActiveKeys.add(event.getCode().toString());
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event)
{
currentlyActiveKeys.remove(event.getCode().toString());
}
});
}
public static void main(String[] args) {
launch(args);
}
}
当我按下箭头按钮时(当然与其他键相同),在范围内我得到的结果如下:
down
down
down
down
down
down
显然只要按下按钮就会发生这种情况。一旦我停止按下它,打印结束。我希望实现的是,一旦按下按钮(无论我持有多长时间),我只会获得down
一次。我需要这个,因为我想更新canvas
中的矩形颜色。
答案 0 :(得分:4)
正在发生的事情是操作系统有一种自动输入功能,这样当你按住一个键时,它会不断产生按键事件,即使你不是真的按键不止一次。
通过为每个键添加按键和键释放处理程序以及布尔状态,您可以跟踪自按键以来是否已处理该键。然后,只要释放该键,就可以重置该处理状态,以便下次真正按下它时可以处理它。
示例应用
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.HashMap;
public class Main extends Application {
private HashMap<String, Boolean> currentlyActiveKeys = new HashMap<>();
@Override
public void start(Stage stage) throws Exception {
final Scene scene = new Scene(new Group(), 100, 100);
stage.setScene(scene);
scene.setOnKeyPressed(event -> {
String codeString = event.getCode().toString();
if (!currentlyActiveKeys.containsKey(codeString)) {
currentlyActiveKeys.put(codeString, true);
}
});
scene.setOnKeyReleased(event ->
currentlyActiveKeys.remove(event.getCode().toString())
);
new AnimationTimer() {
@Override
public void handle(long now) {
if (removeActiveKey("LEFT")) {
System.out.println("left");
}
if (removeActiveKey("RIGHT")) {
System.out.println("right");
}
if (removeActiveKey("UP")) {
System.out.println("up");
}
if (removeActiveKey("DOWN")) {
System.out.println("down");
}
}
}.start();
stage.show();
}
private boolean removeActiveKey(String codeString) {
Boolean isActive = currentlyActiveKeys.get(codeString);
if (isActive != null && isActive) {
currentlyActiveKeys.put(codeString, false);
return true;
} else {
return false;
}
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
您可以声明一个全局布尔值,并将其设为to press =!press。然后每2秒将其重置为真或类似的东西。
if(currentlyActiveKeys.contains("DOWN") && press) {
press = !press;//sets it false
System.out.println("down");
}
答案 2 :(得分:0)
我知道这已经是一个长期的问题了。经过数小时的努力,我只想分享我的发现。我终于找到了一个简单的解决方案,即通过添加清除功能。这就是我的意思
if(currentlyActiveKeys.contains("LEFT")) {
System.out.println("left");
}
if(currentlyActiveKeys.contains("RIGHT")) {
System.out.println("right");
}
if(currentlyActiveKeys.contains("UP")) {
System.out.println("up");
}
if(currentlyActiveKeys.contains("DOWN")) {
System.out.println("down");
}
currentlyActiveKeys.clear();
最后一行将清除当前活动的密钥,因此将防止重复,如原始文章所述
答案 3 :(得分:-1)
在fxml的主容器的“按按键时”定义的方法userPressedAkey()(或您选择的方法的另一个通用名称)下,使用:
public void userPressedAkey(KeyEvent key) {
System.out.println("pressed the key: " + key.getText());
...
}
这样做,您不必担心单个按键被多次处理-它只发生一次。 这里的主要方面是定义:“ KeyEvent键”被定义为方法的参数。