我正在测试关键处理程序,我遇到了一个问题。
最简单的形式,我有以下代码:
mainScene.setOnKeyPressed( event -> {
System.out.println("Handler called for: " + event.getCode());
});
正如预期的那样,当按下某个键时,它会打印出相关的代码。
问题是,如果我一次按住2个键,只按下最后一个键会产生常量事件。我希望能够将按下的键添加到队列中以便在其他地方处理,但只有最后按下的键才会被添加到队列中。
有没有办法改变这种行为?
我能找到的唯一解决方法是使用地图来记录代码,并设置一个单独的按下并释放的处理程序来添加/删除地图中的代码。这可行,但需要不断轮询我可能需要响应的每个键,而不是只能检查按下的键队列是否为空。
答案 0 :(得分:1)
我怀疑JVM正在接收来自操作系统的按键事件,因此当您按住两个键时,重复键行为将在操作系统级别确定。
要管理您自己的按键重复,您可以使用具有无限循环计数的时间轴;按下该键时启动时间线,并在释放该键时将其停止。您可能需要在Map<KeyCode, Timeline>
中管理这些以处理多个密钥。让时间轴调用方法并传递密钥代码以便集中处理按键操作:这将避免轮询的需要。
SSCCE:
import java.util.HashMap;
import java.util.Map;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MultiRepeatKey extends Application {
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Pane(), 400, 400);
Map<KeyCode, Timeline> keyRepeats = new HashMap<>();
Duration keyPressDelay = Duration.millis(200);
scene.setOnKeyPressed(e -> {
if (! keyRepeats.containsKey(e.getCode())) {
Timeline repeat = new Timeline(new KeyFrame(Duration.ZERO, event -> processKey(e.getCode())),
new KeyFrame(keyPressDelay));
repeat.setCycleCount(Animation.INDEFINITE);
repeat.play();
keyRepeats.put(e.getCode(), repeat);
}
});
scene.setOnKeyReleased(e -> {
if (keyRepeats.containsKey(e.getCode())) {
Timeline repeat = keyRepeats.get(e.getCode());
repeat.stop();
keyRepeats.remove(e.getCode());
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void processKey(KeyCode code) {
System.out.println(code.getName());
}
public static void main(String[] args) {
launch(args);
}
}
根据您的使用情况,可能对您有意义的另一个选项是只保留一个Map
密钥以表示您想要的功能,然后保留Set
这些功能的实现。然后使用AnimationTimer
根据按下的键更新UI。 (AnimationTimer
在每次帧渲染时执行其handle
方法;传入的参数是以纳秒为单位的时间戳。)。
显然,如果你有很多映射,你可以在其他地方定义映射,但这就是想法:
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.DoubleFunction;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class MultiRepeatKey extends Application {
@Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(20, 20, 50, 50);
rect.setFill(Color.CORNFLOWERBLUE);
Pane pane = new Pane(rect);
Set<DoubleFunction<Point2D>> motions = new HashSet<>();
Map<KeyCode, DoubleFunction<Point2D>> keyMappings = new HashMap<>();
keyMappings.put(KeyCode.UP, delta -> new Point2D(0, -delta));
keyMappings.put(KeyCode.DOWN, delta -> new Point2D(0, delta));
keyMappings.put(KeyCode.LEFT, delta -> new Point2D(-delta, 0));
keyMappings.put(KeyCode.RIGHT, delta -> new Point2D(delta, 0));
double speed = 150.0 ; // pixels / second
AnimationTimer anim = new AnimationTimer() {
private long lastUpdate = 0 ;
@Override
public void handle(long now) {
if (lastUpdate > 0) {
double elapsedSeconds = (now - lastUpdate) / 1_000_000_000.0 ;
double delta = speed * elapsedSeconds ;
Point2D loc = motions.stream()
.map(m -> m.apply(delta))
.reduce(new Point2D(rect.getX(), rect.getY()), Point2D::add);
loc = clamp(loc, 0, 0, pane.getWidth() - rect.getWidth(), pane.getHeight() - rect.getHeight());
rect.setX(loc.getX());
rect.setY(loc.getY());
}
lastUpdate = now ;
}
};
anim.start();
Scene scene = new Scene(pane, 400, 400);
scene.setOnKeyPressed(e -> motions.add(keyMappings.get(e.getCode())));
scene.setOnKeyReleased(e -> motions.remove(keyMappings.get(e.getCode())));
primaryStage.setScene(scene);
primaryStage.show();
}
private Point2D clamp(Point2D p, double minX, double minY, double maxX, double maxY) {
if (p.getX() < minX) {
p = new Point2D(minX, p.getY());
} else if (p.getX() > maxX) {
p = new Point2D(maxX, p.getY());
}
if (p.getY() < minY) {
p = new Point2D(p.getX(), minY);
} else if (p.getY() > maxY) {
p = new Point2D(p.getX(), maxY);
}
return p ;
}
public static void main(String[] args) {
launch(args);
}
}