2016-05-26 3 views
3

У меня проблема с обнаружением одного нажатия клавиши в JavaFX. Я должен обнаруживать клавиши со стрелками, но каждый раз, когда я нажимаю любую из этих клавиш, часть моего кода вызывается несколько раз. Я понимаю, что это потому, что AnimationTimer() - это цикл, поэтому это причина, но я понятия не имею, как обнаружить однократное нажатие клавиши. В любом случае, вот код:Обнаружение нажатия одной клавиши в JavaFX

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

Это [связанные вопрос] (http://stackoverflow.com/questions/29962395/how-to-write-a-keylistener-for-javafx) может помочь (или может не). Я предполагаю, что вы пытаетесь сделать что-то другое. – jewelsea

+0

@jewelsea Я редактировал код и вставил неправильную версию. Теперь есть 'setOnKeyPressed' – Lisek

+0

@jewelsea При нажатии стрелок герой непрерывно перемещается. В моем примере я хотел бы, чтобы герой просто сделал один шаг, даже если я удерживаю ключ. Это не работает. Удерживая кнопку, я получаю несколько отпечатков. – Lisek

ответ

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

Вы можете объявить глобальное логическое и toogle это как пресс =! Нажмите. Затем каждые 2 секунды сбрасывают его обратно на истину или что-то в этом роде.

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

Не знаю, как я могу обновить прессу до «true». Если вы сделаете это в 'setOnKeyReleased', вы снова получите эту проблему, так как вы все еще держите ключ, а' AnimationTimer() 'обновляется. – Lisek

+0

установить прессование на false в методе setOnKeyReleased, может быть? Значение клавиши можно нажать только один раз и не будет сброшено до отпускания. – Aaron

+0

Да. Это ясно, но как я могу установить 'press' обратно на' true' – Lisek

Смежные вопросы