Come posso implementare un'altra classe nella mia GUI JavaFX?


Ho lavorato a un piccolo gioco di avventura come progetto finale per la mia classe AP Java per un po ' di tempo, e ho cercato di implementare le classi che ho creato per la GUI JavaFX che avevo costruito con Scene Builder.

Ma per qualche motivo, la GUI si rifiuta di riconoscere il mio metodo startAdventure(), nella classe Adventure, che fondamentalmente inizia il tutto. La GUI viene avviata, ma non visualizza correttamente ciò che viene detto di visualizzare in startAdventure().

startAdventure() sembra non essere chiamato a tutto fino a dopo la chiusura della GUI, con conseguente eccezione del puntatore nullo in quanto startAdventure() dovrebbe manipolare l'area di testo e tale nella GUI.

Mi scuso in anticipo se non sto fornendo informazioni sufficienti, non capisco i suggerimenti, ecc. Sono un po ' sfidato quando si tratta di codifica, e sono completamente nuovo a pubblicare cose su questo sito.

Ecco come appare l'inizio di startAdventure() nella mia classe Adventure (text e inputText sono variabili inizializzate correttamente dal mio AdventureGUI.fxml file, che è stato realizzato utilizzando Scene Builder):

public void startAdventure()
{
    //welcome user
    text.setText("Welcome to the land of euphoria!" + "\n");
    //gets users name
    text.appendText("What's your name?" + "\n");
    p.playerName = inputText.getText();
    text.appendText("Welcome, " + p.playerName + "\n");

    //sets start location
    p.setNewLoc("START");

Ed ecco come appare la mia classe principale:

public class RunAdventure extends Application
{

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

    Parent root = FXMLLoader.load(getClass().getResource("AdventureGUI.fxml"));
    primaryStage.setScene(new Scene(root, 600, 600));
    primaryStage.setTitle("Adventure");
    primaryStage.show();
}

public static void main(String[] args)
{
    Adventure game = new Adventure();
    launch(args);
    game.startAdventure();
}
} //end class

Se funzionasse correttamente, la GUI si avvierebbe e all'interno dell'Area di testo visualizzerebbe il testo che inizia con "Benvenuti nella terra dell'euforia!".

Tuttavia, nel suo stato attuale, la GUI viene avviata e non ha alcun testo nell'area di testo.

Ancora una volta, nuovo a tutto qui, quindi fammi sapere subito se pensi che ci sia qualcos'altro che devi mostrare per aiutare il processo di suggerimento.

Edit: Quando ho tentato di cambiare l'ordine di dove gioco.startAdventure () era, mettendolo proprio prima di primaryStage.show () nel metodo start, si è verificato un lungo elenco di eccezioni. Ecco il cambiamento che ho fatto:

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

        Parent root = FXMLLoader.load(getClass().getResource("AdventureGUI.fxml"));
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.setTitle("Adventure");
        Adventure game = new Adventure();
        game.startAdventure();
        primaryStage.show();
    }

E qui ci sono le eccezioni che si sono verificate:

Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:894)
    at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:56)
    at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:158)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
    at Classwork.FinalExamProject.Adventure.startAdventure(Adventure.java:102)
    at Classwork.FinalExamProject.RunAdventure.start(RunAdventure.java:24)
    at com.sun.javafx.application.LauncherImpl$8.run(LauncherImpl.java:837)
    at com.sun.javafx.application.PlatformImpl$7.run(PlatformImpl.java:335)
    at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:301)
    at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:298)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:298)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)

Dovrei anche menzionare che la classe Adventure funge anche da controller

EDIT (5/20): Va bene, ho ho ottenuto la maggior parte di tutto nel mio programma, ma ora sto incontrando un nuovo problema. Nella mia avventura, ho una funzione che permette al giocatore di combattere vari mostri se si trovano in una determinata posizione. Il mio problema è che una volta arrivato a questa posizione nel mio programma, il programma smette di rispondere. Dopo aver fatto qualche debug, ho capito che era un ciclo while che avevo che fa tutto nel metodo doBattle() mentre il giocatore.isAlive e nemico.E ' vivo. L'unico problema è, una volta rimosso il while loop, il programma salta subito dopo che il mostro è morto o il giocatore è morto, mentre dovrebbe darti la scelta se attaccare il mostro o meno ripetutamente fino a quando entrambe le entità non sono morte. So che sto stipando troppe cose in questa domanda, ma non so dove altro metterlo, e finora ho ottenuto un sostegno schiacciante da tutti qui. Non posso ringraziarvi abbastanza, ma non ho idea di come risolvere questo da solo. Ecco il codice del Metodo doBattle ():

public void doBattle(Monster enemy)
    {
        //boolean fled = false;
        //Greets player into battle by telling what monster they are fighting
        text.appendText("\n" + "A wild " + enemy.getName() + " has appeared!" + "\n" );
        mobImagePane.setImage(enemy.getImage());

        //System.out.print("THIS RAN"); //debug


        while ( p.getHealth() > 0 && enemy.getHealth() > 0 ) //while enemy and player are alive
        {
            //Prompts user to attack or run
            text.appendText("Attack " + enemy.getName() + "? (Y/N) " + "\n");
            inputText.setOnAction(event ->
            {
                String fightChoice = inputText.getText();
                fightChoice = fightChoice.toUpperCase();
                //String fightChoice = this.choice;


                if (fightChoice.equals("Y"))//if they want to fight
                {
                    //Player turn
                    enemy.setHealth(enemy.getHealth() - p.getDamage()); //Sets the monsters health as their current health minus the players damage
                    text.appendText("You attack " + enemy.getName() + " for " + p.getDamage() + " damage!" + "\n" + "Enemy health is " + enemy.getHealth() + "\n");

                    //Monster turn
                    p.setHealth(p.getHealth() - enemy.getDamage()); //Sets the players health as their current health minus the monsters damage
                    text.appendText("The " + enemy.getName() + " hit you for " + enemy.getDamage() + " damage!" + "\n" + "Your health is " + p.getHealth() + "\n"); //prints how much damage the monster does to the player
                    if (p.health < 20.0) {
                        text.appendText("Your health is low, you should return home and restore health!" + "\n");
                    }

                    //checks if the player or monster is dead
                    this.checkLife();
                    enemy.checkLife();

                } else {
                    if (fightChoice.equals("N")) // if they don't want to fight
                    {
                        mobImagePane.setImage(null);
                        text.appendText("You fled from the fight!" + "\n");
                        this.setNewLoc("TOWN"); // brings you back to town
                        fled = true;
                        //JOptionPane.showMessageDialog(null, "You are now in " + this.currentLoc.getName() + "\n" + this.currentLoc.getDescription());
                        //String move2 = JOptionPane.showInputDialog(null,"Which way do you want to go? (N, E, S, W)");
                        //makeMove(move2);
                        //break;
                    } else // they don't make any sense
                    {
                        text.appendText("Unrecognized command" + "\n");
                    }
                }

                //}

                //when someone dies
                if (!fled) {
                    if (p.alive) // if you are still standing
                    {
                        //print results (money earned, health remaining)
                        mobImagePane.setImage(null);
                        p.wallet += enemy.getLoot();
                        playerInfo.setText(p.getPlayerName() + "\n" + "Health: " + p.getHealth() + "\n" + "Wallet: " + p.getWallet() + "\n");
                        text.setText("You shrekt the " + enemy.getName() + "\n" + "You got $" + enemy.getLoot() + " for winning!" + "\n" + "You now have $" + p.wallet + "\nYour health is " + p.getHealth() + "\n");
                    } else //if you died
                    {
                        mobImagePane.setImage(null);
                        text.setText("You have been shrekt by the " + enemy.getName() + "\n" + "GAME OVER" + "\n");
                        text.appendText("\nPlay again? (Y/N)" + "\n");
                        inputText.setOnAction(event2 -> {
                            String answer = inputText.getText();
                            answer.toUpperCase();
                            //String answer = this.choice;

                            if (answer.equals("Y")) //if they want to play again
                            {
                                text.appendText("Alright! Let's go!" + "\n");
                                this.reset();
                            } else //if they want to quit
                            {
                                text.appendText("Wow. What a skrub, okay bye." + "\n");
                                System.out.close();
                            }
                        });

                    }
                }
            });
        }
    } //end doBattle
Author: Andrew Brook, 2016-05-16

1 answers

(Alcune di) le cose che stanno andando male e come risolverle

Come codificato, il tuo metodo startAdventure() non viene chiamato fino a dopo l'arresto del sistema JavaFX, che ovviamente non è quello che vuoi.

Il metodo di avvio javadoc afferma:

Il metodo di avvio non ritorna fino a quando l'applicazione non è uscita, tramite una chiamata a Platform.exit o tutte le finestre dell'applicazione sono state chiuse.

Stai invocando startAdventure() al momento sbagliato, è necessario richiamarlo nel metodo start() dell'applicazione anziché dopo la chiamata al metodo launch().

game.startAdventure();
primaryStage.show();

Inoltre, Java launcher crea un'istanza dell'applicazione, quindi non è necessario crearne una in modo esplicito. Leggere il JavaFX application lifecycle per comprendere questo processo. Quindi il tuo metodo principale dovrebbe essere:

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

Alcune speculazioni da parte mia

Inoltre, la creazione di un nuovo Adventure() è un po ' sospetto, anche se è difficile dirlo con certezza senza ulteriore codice fornito. Adventure potrebbe essere un controller, quindi forse non è necessario creare un nuovo oggetto Adventure, ma è possibile ottenere il controller dal caricatore come descritto in:

E, se si tratta di un controller, il codice per iniziare l'avventura potrebbe verificarsi nel metodo initialize() del controller, che verrà automaticamente richiamato dal FXMLLoader.

Alla fine, man mano che l'applicazione cresce, potrebbe essere utile creare un approccio in stile MVC, in cui la classe Adventure è solo dati e logica del modello che vengono passati in un AdventureView che è un controller che collega il modello e la vista FXML insieme. Sebbene la discussione di tecniche dettagliate su come ottenere ciò sia al di fuori dello scopo di questa risposta, potresti ottenere alcune idee dalla risposta dei parametri di passaggio collegata in precedenza.

Risposte a domande complementari

Text e inputText sono variabili inizializzate correttamente dal mio AdventureGUI.file fxml, che è stato realizzato utilizzando Scene Builder

Le variabili nella tua istanza della classe Adventure non verranno inizializzate da FXMLLoader se crei la tua classe adventure usando new, invece FXMLLoader creerà la propria istanza di Adventure e inizializzerà quella, e che sarà separata dall'istanza che hai creato. Come il testo campo non è inizializzato per l'istanza, si finisce con un NullPointerException .

Inoltre, text e inputText sono stati dichiarati nella classe Adventure in questo modo: '@ FXML private TextArea text; @ FXML private TextArea inputText; '

Sì, devono essere creati da FXMLLoader, che non si verificherà se scrivi semplicemente new Adventure().

Quindi per risolvere tutto questo, ho bisogno di creare un metodo initialize() nella mia classe controller? Come potrei andare farlo?

Opzione A (lascia che FXMLLoader inizializzi implicitamente la tua avventura)

Rinominare startAdventure() in initialize().

public void start(Stage primaryStage) throws Exception
{
    Parent root = FXMLLoader.load(getClass().getResource("AdventureGUI.fxml"));
    primaryStage.setScene(new Scene(root, 600, 600));
    primaryStage.setTitle("Adventure");
    primaryStage.show();
}

Opzione B (inizializza esplicitamente la tua avventura)

public void start(Stage primaryStage) throws Exception
{
    FXMLLoader loader = new FXMLLoader(
        getClass().getResource("AdventureGUI.fxml")
    );
    Parent root = loader.load();
    Adventure adventure = loader.<Adventure>getController();
    adventure.startAdventure();
    primaryStage.setScene(new Scene(root, 600, 600));
    primaryStage.setTitle("Adventure");
    primaryStage.show();
}

Campione Applicazione

esempio di app

avventura / AdventureApp.java

package adventure;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class AdventureApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(
                getClass().getResource(
                        "AdventureGUI.fxml"
                )
        );
        Parent root = loader.load();

        stage.setScene(new Scene(root));
        stage.setTitle("Adventure");
        stage.show();
    }

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

avventura / Avventura.java

package adventure;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

public class Adventure {
    @FXML
    private TextArea text;

    @FXML
    private TextField inputText;

    private Player player = new Player();

    public void initialize() {
        text.setText("Welcome to the land of euphoria!\n");
        text.appendText("What's your name?\n");

        Platform.runLater(() -> inputText.requestFocus());
        inputText.setOnAction(event -> {
            player.setName(inputText.getText());
            text.appendText("Welcome, " + player.getName() + "\n");
        });

        player.setLocation("START");
    }
}

avventura / Giocatore.java

package adventure;

public class Player {
    private String name;
    private String location;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }
}

avventura / Avventura.java

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

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="200.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="adventure.Adventure">
   <children>
      <TextArea fx:id="text" editable="false" prefHeight="200.0" prefWidth="200.0" wrapText="true" VBox.vgrow="ALWAYS" />
      <TextField fx:id="inputText" minHeight="-Infinity" />
   </children>
   <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
   </padding>
</VBox>
 2
Author: jewelsea, 2017-05-23 12:16:04