【问题标题】:Detect single key press in JavaFX检测 JavaFX 中的单键按下
【发布时间】:2016-09-25 03:10:00
【问题描述】:

我在检测 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 中矩形的颜色。

【问题讨论】:

  • 这个related question 可能有帮助(也可能没有)。我的猜测是你正在尝试做一些不同的事情。
  • @jewelsea 我正在编辑代码并粘贴了错误的版本。现在有setOnKeyPressed
  • @jewelsea 按下箭头时英雄不断移动。在我的示例中,即使我按住键,我也想成为英雄只是迈出一步。这是行不通的。按住按钮时,我得到了多张照片。
  • 是的,我做到了。它没有帮助。这是因为在下一个AnimationTimer 循环中,当您按下按钮时,它将再次从setOnKeyPressed 添加到Set

标签: java javafx keyboard-events


【解决方案1】:

发生的情况是操作系统具有一种自动键入功能,当您按住某个键时,它会不断生成按键事件,即使您实际上并没有多次按下该键。

通过为每个键添加按键和按键释放处理程序以及布尔状态,您可以跟踪您是否在按键被按下后对其进行了处理。然后,您可以在释放键时重置该处理状态,以便下次真正按下它时可以处理它。

示例应用程序

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);
    }
}

【讨论】:

    【解决方案2】:

    您可以声明一个全局布尔值并像 press = !press 一样对其进行转换。然后每 2 秒将其重置为 true 或类似的效果。

    if(currentlyActiveKeys.contains("DOWN") && press) {
           press = !press;//sets it false
           System.out.println("down");
     }
    

    【讨论】:

    • 不确定如何将媒体更新回true。如果您在setOnKeyReleased 中执行此操作,您将再次遇到此问题,因为您在AnimationTimer() 刷新时仍然持有密钥。
    • 在 setOnKeyReleased 方法中将 press 设置为 false 可能吗?这意味着该键只能按一次,并且在释放之前不会重置。
    • 是的。这很清楚,但我如何将press 设置回true
    【解决方案3】:

    我知道这已经是一个很长的问题了。经过数小时的挣扎,我只想分享我的发现。我终于找到了一个简单的解决方案,即添加 clear 函数。这就是我的意思

    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();
    

    最后一行将清除当前活动的密钥,因此它将防止重复,如原帖中所述

    【讨论】:

      猜你喜欢
      • 2017-02-16
      • 1970-01-01
      • 2018-10-24
      • 2018-09-24
      • 2014-08-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-17
      • 1970-01-01
      相关资源
      最近更新 更多