JavaFX key interruptions?

Deadly 提交于 2019-11-29 12:59:58
James_D

This is similar to this question, though the requirements look slightly different. Similar techniques work, but if you just want to respond to the most-recently-pressed key, you probably need some kind of stack.

This example allows you to move a rectangle around the screen, with the up, down, left, or right cursor keys; only the most recently pressed key that is still down is used.

import java.util.LinkedList;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class KeyStackExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        Rectangle rect = new Rectangle(50, 50, 100, 50);
        rect.setFill(Color.SALMON);

        Pane pane = new Pane(rect);
        Scene scene = new Scene(pane, 800, 600);

        final double rectangleHSpeed = 100 ; // pixels per second
        final double rectangleVSpeed = 100 ;
        final double minX = 0 ;
        final double maxX = 800 ; 
        final double minY = 0 ;
        final double maxY = 600 ;

        final LinkedList<KeyCode> keyStack = new LinkedList<>();
        scene.setOnKeyPressed(event -> {
            KeyCode code = event.getCode();
            if (! keyStack.contains(code)) {
                keyStack.push(code); 
            }
        });

        scene.setOnKeyReleased(event -> 
            keyStack.remove(event.getCode()));


        final LongProperty lastUpdateTime = new SimpleLongProperty();
        final AnimationTimer rectangleAnimation = new AnimationTimer() {
          @Override
          public void handle(long timestamp) {
            if (! keyStack.isEmpty() && lastUpdateTime.get() > 0) {
              final double elapsedSeconds = (timestamp - lastUpdateTime.get()) / 1_000_000_000.0 ;
              double deltaX = 0 ;
              double deltaY = 0 ;
              switch(keyStack.peek()) {
              case UP:
                  deltaY = -rectangleVSpeed * elapsedSeconds;
                  break ;
              case DOWN: 
                  deltaY = rectangleVSpeed * elapsedSeconds ;
                  break ;
              case LEFT:
                  deltaX = -rectangleHSpeed * elapsedSeconds ;
                  break ;
              case RIGHT:
                  deltaX = rectangleHSpeed * elapsedSeconds ;
              default:
                  break ;
              }
              double oldX = rect.getTranslateX() ;
              double oldY = rect.getTranslateY() ;
              rect.setTranslateX(clamp(oldX + deltaX, minX, maxX));
              rect.setTranslateY(clamp(oldY + deltaY, minY, maxY));
            }
            lastUpdateTime.set(timestamp);
          }
        };
        rectangleAnimation.start();

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private double clamp(double value, double min, double max) {
        return Math.max(min, Math.min(max, value));
    }

    public static void main(String[] args) {
        launch(args);
    }
}

I would make a set of all keys currently pressed and just check them in a timer. It doesn't eat many cpu cycles on my old machine. In motion it's 5%, but 0% when no key is pressed.

For a possibly faster solution use an ObservableSet and start and stop actions when a key is added or removed.

package keytest;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class KeyTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        ObservableSet<KeyCode> downKeys = FXCollections.observableSet();

        Circle circle = new Circle(10);
        Pane root = new Pane(circle);
        Scene scene = new Scene(root, 500, 500);

        scene.setOnKeyPressed(evt->{
            downKeys.add(evt.getCode());
        });

        scene.setOnKeyReleased(evt->{
            downKeys.remove(evt.getCode());
        });


        Timeline timer = new Timeline(new KeyFrame(
            javafx.util.Duration.millis(16), ae -> {
                downKeys.stream().parallel().forEach(kc -> {
                    Platform.runLater(() -> {//why?
                        switch(kc){
                            case UP: circle.setTranslateY(circle.getTranslateY()-2);
                                break;
                            case DOWN: circle.setTranslateY(circle.getTranslateY()+2);
                                break;
                            case LEFT: circle.setTranslateX(circle.getTranslateX()-2);
                                break;
                            case RIGHT: circle.setTranslateX(circle.getTranslateX()+2);
                                break;
                        }
                    });
                });
            }));
        timer.setCycleCount(Animation.INDEFINITE);
        timer.play();

        //I think this might be faster
        downKeys.addListener((SetChangeListener.Change<? extends KeyCode> change) -> {
            if (change.wasAdded()){
                System.out.println("start action "+change.getElementAdded());
            }
            if (change.wasRemoved()){
                System.out.println("stop action "+change.getElementRemoved());
            }
        });

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

I'm thinking the parallel is stupid and the Platform.runLater costs more than the threading of +2 or -2 . But hey, java8 that's why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!