如何在JavaFX GUI中实现另一个类?

时间:2016-05-16 17:44:59

标签: java user-interface javafx scenebuilder

我一直在做一个小冒险游戏作为我的AP Java课程的最后一个项目,我已经尝试实现我为{{1}所做的课程了。我使用JavaFX构建的GUI。

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

在GUI关闭之前,

startAdventure()似乎根本没有被调用,导致空指针异常,因为startAdventure()应该在GUI中操纵文本区域等。

如果我没有提供足够的信息,不了解建议等,我会提前道歉。在编码方面我有点挑战,而且我在这个网站上发布内容我是全新的。

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

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

但是,在目前的状态下,GUI会启动,文本区域根本没有文字。

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

编辑:当我尝试更改game.startAdventure()所在的顺序时,通过将其放在start方法中的primaryStage.show()之前,发生了很长的异常列表。 以下是我所做的改变:

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

以下是发生的例外情况:

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

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

编辑(5月20日):好的,我已经把大部分工作都放在了我的程序中,但我现在遇到了一个新问题。在我的冒险中,我有一个功能,允许玩家在各个怪物的战斗中,如果他们在一个给定的位置。我的问题是,一旦我到达我的程序中的这个位置,该程序停止响应。在做了一些调试后,我发现它是一个while循环,我在doBattle()方法中执行所有操作,而player.isAlive和enemy.isAlive。唯一的问题是,一旦我移除了while循环,程序只是在怪物死亡或玩家死亡之后直接跳转,而它应该让你选择是否反复攻击怪物,直到任一实体是死的。我知道我在这个问题上填写了太多东西,但我不知道还有什么地方可以提出来,到目前为止,我得到了大家的支持。我不能够感谢你们,但我不知道如何自己解决这个问题。 这是doBattle()方法的代码:

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)

1 个答案:

答案 0 :(得分:2)

(某些)出错的地方以及如何解决这些问题

按照编码,直到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对象,而是可以从加载器中获取控制器,如下所述:

并且,如果它是一个控制器,那么启动冒险的代码可能发生在控制器的initialize()方法中,这将由FXMLLoader自动调用。

最终随着应用程序的增长,创建MVC样式的方法可能是有益的,其中Adventure类只是模型数据和逻辑,它被传递到AdventureView,AdventureView是一个控制器,它将模型和FXML视图连接在一起。虽然有关如何实现这一目标的详细技术的讨论超出了本答案的范围,但您可能会从之前链接的传递参数答案中获得一些想法。

其他跟进问题的答案

  

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

如果你使用new创建冒险类,FXMLLoader将不会初始化Adventure类实例中的变量,而FXMLLoader将创建它自己的Adventure实例并初始化它,这将是独立的来自您创建的实例。由于未针对您的实例初始化文本字段,因此您最终得到NullPointerException

  

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

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

  

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

选项A(让FXMLLoader隐式初始化你的冒险)

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(明确初始化冒险)

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

示例应用

sample app

冒险/ 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.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");
    }
}

冒险/ 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.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>