为什么keyeevents只在最后按下键时生成?

  • 本文关键字:最后 keyeevents javafx-8
  • 更新时间 :
  • 英文 :


我正在测试键处理程序,我遇到了一个问题。

在最简单的形式中,我有以下代码:
mainScene.setOnKeyPressed( event -> {
    System.out.println("Handler called for: " + event.getCode());
});

如预期的那样,当按下一个键时,它打印出相关的代码。

问题是,如果我同时按住两个键,只有最后按下的键产生恒定的事件。我希望能够将按下的键添加到队列中,以便在其他地方处理,但只有最后按下的键才会添加到队列中。

有办法改变这种行为吗?

我能找到的唯一解决方法是使用地图来记录代码,并设置一个单独的按下和释放处理程序来从地图中添加/删除代码。这是可行的,但需要不断轮询我可能需要响应的每个键,而不是仅仅检查按下的键队列是否为空。

我怀疑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);
    }
}

相关内容

  • 没有找到相关文章