【问题标题】:How can I implement another class into my JavaFX GUI?如何在我的 JavaFX GUI 中实现另一个类?
【发布时间】:2016-05-16 17:44:59
【问题描述】:

我一直在为我的 AP Java 课程做一个小型冒险游戏作为最后一个项目,我一直在尝试实现我为 JavaFX GUI 制作的类使用Scene Builder 构建。

但由于某种原因,GUI 拒绝识别我在 Adventure 类中的方法 startAdventure(),它基本上启动了整个过程。 GUI 已启动,但无法正确显示 startAdventure() 中告知的内容。

startAdventure() 似乎直到 GUI 关闭后才被调用,导致空指针异常,因为 startAdventure() 应该在 GUI 中操作文本区域等。

如果我没有提供足够的信息,不理解建议等,我提前道歉。在编码方面我有点挑战,而且我对在这个网站上发布东西完全陌生。

这是 startAdventure() 的开头在我的 Adventure 类中的样子(text 和 inputText 是我的 AdventureGUI.fxml 文件中正确初始化的变量,它是使用 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");

这是我的主要课程的外观:

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

如果工作正常,GUI 将启动并在文本区域内显示以“欢迎来到欣快之地!”开头的文本

但是,在当前状态下,GUI 已启动,并且在文本区域中根本没有文本。

再一次,这里的一切都是新的,所以如果你认为还有其他需要我展示的东西来帮助建议过程,请立即告诉我。

编辑:当我尝试更改 game.startAdventure() 的顺序时,将它放在 start 方法中的 primaryStage.show() 之前,出现了一长串异常。 这是我所做的更改:

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

以下是发生的异常情况:

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)

我还应该提到 Adventure 类也可以用作控制器

编辑 (5/20):好的,我的程序中的大部分内容都已完成,但我现在遇到了一个新问题。在我的冒险中,我有一个功能可以让玩家在给定位置与各种怪物战斗。我的问题是,一旦我到达程序中的这个位置,程序就会停止响应。在进行了一些调试之后,我发现这是一个 while 循环,我在 doBattle() 方法中执行了 player.isAlive 和enemy.isAlive 中的所有操作。唯一的问题是,一旦我删除了 while 循环,程序就会直接跳转到怪物死亡或玩家死亡之后,而它应该让你选择是否重复攻击怪物,直到任一实体是死的。我知道我在这个问题上塞进了太多东西,但我不知道还能把它放在哪里,到目前为止,我已经得到了这里每个人的压倒性支持。我不能感谢你们,但我不知道如何自己解决这个问题。 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

【问题讨论】:

  • 你试过将game.startAdventure();指令放在launch(args);之前吗?

标签: java user-interface javafx scenebuilder


【解决方案1】:

(部分)出错的地方以及如何解决它们

按照编码,直到 JavaFX 系统关闭后才会调用您的 startAdventure() 方法,这显然不是您想要的。

launch 方法 javadoc 指出:

在应用程序退出之前,启动方法不会返回,无论是通过调用Platform.exit 还是所有应用程序窗口都已关闭。

您在错误的时间调用startAdventure(),您应该在应用程序的start() 方法中调用它,而不是在launch() 方法调用之后。

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

此外,Java 启动器会创建应用程序的一个实例,因此您无需显式创建一个。阅读JavaFX application lifecycle 以了解此过程。所以你的主要方法应该是:

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

我的一些猜测

此外,您创建新的Adventure() 有点可疑,但如果没有提供进一步的代码,很难确定。 Adventure 可能是一个控制器,所以也许您根本不需要创建一个新 Adventure 对象,而是可以从加载器中获取控制器,如下所述:

如果是控制器,那么开始冒险的代码可能出现在控制器的initialize()方法中,FXMLLoader会自动调用该方法。

最终随着应用程序的增长,创建 MVC 风格的方法可能会有所帮助,其中 Adventure 类只是模型数据和逻辑,它们被传递到 AdventureView 中,AdventureView 是一个将模型和 FXML 视图连接在一起的控制器。尽管有关如何完成此任务的详细技术的讨论超出了此答案的范围,但您可能会从前面链接的传递参数答案中获得一些想法。

其他后续问题的答案

text 和 inputText 是从我的 AdventureGUI.fxml 文件中正确初始化的变量,该文件是使用 Scene Builder 制作的

如果您使用 new 创建冒险类,则冒险类实例中的变量将不会被 FXMLLoader 初始化,而是 FXMLLoader 将创建它自己的 Adventure 实例并对其进行初始化,这将与您创建的实例。由于没有为您的实例初始化文本字段,因此您最终会得到 NullPointerException

另外,在 Adventure 类中的 text 和 inputText 声明如下:'@FXML private TextArea text; @FXML private TextArea inputText;'

是的,它们需要由FXMLLoader创建,如果你只写new Adventure()就不会发生。

所以要解决这一切,我需要在我的控制器类中创建一个 initialize() 方法吗?我该怎么做呢?

选项 A(让 FXMLLoader 隐式初始化您的 Adventure)

startAdventure() 重命名为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();
}

选项 B(显式初始化您的 Adventure)

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

示例应用程序

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

adventure/Adventure.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");
    }
}

adventure/Player.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;
    }
}

adventure/Adventure.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>

【讨论】:

  • 感谢您的快速响应,我立即尝试了,但遗憾的是它没有奏效。我写道:冒险游戏 = new Adventure();和 game.startAdventure();就在 primaryStage.show() 之前,但它只是导致了看似无穷无尽的错误。
  • 如果您想要更好的答案,您需要supply more code 和错误输出,以便有人可以合理地分析该方法并复制结果。
  • 感谢您将堆栈跟踪和其他信息添加到您的问题中。您收到 NullPointerException 的原因已在更新的答案和链接的 StackOverflow NullPointerException Q&A 中进行了解释。请注意,您可能在未提供的代码中遇到其他错误。
  • 所以要解决这一切,我需要在我的控制器类中创建一个 initialize() 方法吗?我该怎么做呢?此外,在 Adventure 类中声明了 text 和 inputText,如下所示:'@FXML private TextArea text; @FXML private TextArea inputText;'
  • 查看解决其他问题的更新答案。
猜你喜欢
  • 2021-07-04
  • 2014-05-21
  • 1970-01-01
  • 2015-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-12
  • 1970-01-01
相关资源
最近更新 更多