'JavaFX how to use AnimationTimer in separate class?

I've seen many examples that write AnimationTimer and KeyListener in Main class but not seen in another class.

I've tried How to write a KeyListener for JavaFX in "VolleyController", but I don't know why it did not work.

First, I used KeyEvent to move the image just like the first code. The third code is my Main class. I want to rewrite the methods to move images just like the second code.

But I went into some error written in the second code. How can I deal with it?

import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;

public class VolleyController implements EventHandler<KeyEvent> {
    double pika1LocationY = 556;
    double pika2LocationY = 556;
    double pika1LocationX = 0;
    double pika2LocationX = 1050;
    
    boolean pika2MoveLeft = false;  
    boolean pika2MoveRight = false;
    boolean pika1MoveLeft = false;
    boolean pika1MoveRight = false;
    
    @FXML
    ImageView pika1,pika2,ball;
    
    @Override
    public void handle(KeyEvent e) {
        switch (e.getCode()) {
        case ESCAPE:
            Main.currentStage.setScene(Main.menuScene);
            break;
        case UP:
            break;
        case LEFT:
            pika2MoveLeft = true;
            break;
        case RIGHT:
            pika2MoveRight = true;
            break;
        case T:
            break;
        case F:
            pika1MoveLeft = true;
            break;
        case H:
            pika1MoveRight = true;
            break;

        }   
        move();
    }
    
    public void released(KeyEvent r) {
        switch (r.getCode()) {
        case LEFT:
            pika2MoveLeft = false;
            break;
        case RIGHT:
            pika2MoveRight = false;
            break;
        case F:
            pika1MoveLeft = false;
            break;
        case H:
            pika1MoveRight = false;
            break;
        }
        
    }
    
    public void move() {
        if(pika1MoveLeft && pika1.getLayoutX()>=0) 
            pika1.setLayoutX(pika1.getLayoutX()-10);
        if(pika1MoveRight && pika1.getLayoutX()<=440 ) 
            pika1.setLayoutX(pika1.getLayoutX()+10);
        if(pika2MoveLeft && pika2.getLayoutX()>600) 
            pika2.setLayoutX(pika2.getLayoutX()-10);
        if(pika2MoveRight && pika2.getLayoutX()<1050) 
            pika2.setLayoutX(pika2.getLayoutX()+10);
    }
    
}
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;

public class VolleyController implements EventHandler<KeyEvent> {
    double pika1LocationY = 556;
    double pika2LocationY = 556;
    double pika1LocationX = 0;
    double pika2LocationX = 1050;
    
    boolean pika2MoveLeft = false;  
    boolean pika2MoveRight = false;
    boolean pika1MoveLeft = false;
    boolean pika1MoveRight = false;
    
    @FXML
    ImageView pika1,pika2,ball;
                    //this gives me a syntax error about getScene(), but the following did not
    Main.currentStage.getScene().setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent e) {

            switch (e.getCode()) {
            case ESCAPE:
                Main.currentStage.setScene(Main.menuScene);
                break;
            case UP:
                break;
            case LEFT:
                pika2MoveLeft = true;
                break;
            case RIGHT:
                pika2MoveRight = true;
                break;
            case T:
                break;
            case F:
                pika1MoveLeft = true;
                break;
            case H:
                pika1MoveRight = true;
                break;

            }   
    }}); //this told me to delete tokens
  
                  //this did not give me a syntax error
    Main.currentStage.getScene().setOnKeyReleased(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent r) {
            switch (r.getCode()) {
            case LEFT:
                pika2MoveLeft = false;
                break;
            case RIGHT:
                pika2MoveRight = false;
                break;
            case F:
                pika1MoveLeft = false;
                break;
            case H:
                pika1MoveRight = false;
                break;
            }
        }
    });
    
    AnimationTimer timer=new AnimationTimer() {
        @Override
        public void handle(long now)
        {   
            int pika1dx=0, pika2dx=0;
            
            if(pika1MoveLeft) pika1dx-=10;
            if(pika1MoveRight) pika1dx+=10;
            if(pika2MoveLeft) pika2dx-=10;
            if(pika2MoveLeft) pika2dx-=10;
            
            PikaMoveBy(pika1dx,pika2dx);
            
        }
    };
    
    timer.start();
}
public void PikaMoveBy(int pika1dx,int pika2dx)
{
    if(pika1dx==0 || pika2dx==0) return;
    
    final double pika1cx=pika1.getBoundsInLocal().getWidth()/2; 
    final double pika2cx=pika2.getBoundsInLocal().getWidth()/2;
    
    double pika1x=pika1cx+pika1.getLayoutX()+pika1dx;
    double pika2x=pika2cx+pika2.getLayoutX()+pika2dx;
    
    PikaMoveTo(pika1x, pika2x);
    
}

public void PikaMoveTo(double pika1x, double pika2x)
{
    final double pika1cx=pika1.getBoundsInLocal().getWidth()/2;
    final double pika2cx=pika2.getBoundsInLocal().getWidth()/2;
    
    if(pika1x-pika1cx>=0 && pika1x+pika1cx<450 && pika2x-pika2cx>600 && pika2x+pika2cx<1050)
    {
        pika1.relocate(pika1x-pika1cx, pika1_lct_y);
        pika2.relocate(pika2x-pika2cx, pika2_lct_y);
    }
}
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Main extends Application {
    
    public static Stage currentStage;
    public static Scene menuScene;

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        currentStage=primaryStage;
        Parent root=FXMLLoader.load(getClass().getResource("Menu.fxml"));
        Scene MenuScene = new Scene(root);
        currentStage.setScene(MenuScene); //use setScene() to switch the scene
        currentStage.setTitle("PikaVolley");
        currentStage.show();
                
    }
}


Solution 1:[1]

To get key handlres and AnimationTimer working use a simple FXML (Volley.fxml):

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>

<VBox spacing="10.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="test.VolleyController">
   <children>
      <Label alignment="CENTER" contentDisplay="CENTER" prefHeight="17.0" prefWidth="248.0" text="Press Start.Use Arows to set direction" />
      <Pane  prefHeight="250.0" prefWidth="250.0">
         <children>
            <ImageView fx:id="ball" fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true" />
         </children>
      </Pane>
      <Button fx:id="startBtn" mnemonicParsing="false" onAction="#start" text="Start" />
   </children>
</VBox>

And its controller (see comments):

import javafx.animation.AnimationTimer;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;

public class VolleyController implements EventHandler<KeyEvent> {

    //always use publicly available resources when posting
    private static final String BALL = "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/32x32/Circle_Red.png";
    boolean moveRight = false, moveLeft = false, moveUp = false, moveDown = false;
    private static final int DX = 1, DY = 1;

    @FXML
    ImageView ball;
    @FXML 
    Button startBtn;

    @FXML
    void initialize(){
        ball.setImage(new Image(BALL)); //initialize image view
    }

    @FXML
    void start(){

        ball.getScene().setOnKeyPressed(this);  //add Key press and release handlers to scene
        ball.getScene().setOnKeyReleased(this);

        //construct and invoke AnimationTimer
        AnimationTimer timer = new AnimationTimer(){
            @Override
            public void handle(long now) {
                move();  //repeatedly invoke move
            }
        };
        timer.start();

        startBtn.setDisable(true);
    }

    @Override
    public void handle(KeyEvent e) {
         moveRight = false; moveLeft = false;  moveUp = false; moveDown = false;
        //change movement directions based on key events
        switch (e.getCode()) {
            case UP:
                moveUp = true;
                break;
            case LEFT:
                moveLeft = true;
                break;
            case RIGHT:
                moveRight = true;
                break;
            case DOWN:
                moveDown = true;
                break;
        }
    }

    //move if any of the direction control booleans is true
    private void move() {
        if(moveLeft) {
            ball.setLayoutX(ball.getLayoutX()-DX);
        }else  if(moveRight) {
            ball.setLayoutX(ball.getLayoutX()+DX);
        }else if(moveDown) {
            ball.setLayoutY(ball.getLayoutY()+DY);
        }else if(moveUp) {
            ball.setLayoutY(ball.getLayoutY()-DY);
        }
    }
}

test it using :

public class Main extends Application {

    @Override
    public void start(Stage currentStage) throws Exception {

        Parent root=FXMLLoader.load(getClass().getResource("Volley.fxml"));
        currentStage.setScene(new Scene(root));
        currentStage.show();
    }

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1