質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.20%

JavaFXでの画面遷移がわからないです。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,452

hositaka

score 14

前提・実現したいこと

JavaFXを使って画面遷移をさせたいのですが、やり方がよくわからないです。

発生している問題・エラーメッセージ

java.lang.NullPointerException
    <略>

```

該当のソースコード

Original.java

package fx;  

<略>  

/**  
*  
* @author takahara  
*/  
public class Original extends Application {  

private static Original instance;  

private Stage stage;  
private Stage stage2;  
@Override  
public void start(Stage primaryStage) throws Exception {  

try {  
FXMLLoader loader = new FXMLLoader(getClass().getResource("hello.fxml"));  
Parent root = loader.load(); // fx:rootに紐付いたノードインスタンスが得られる  
HelloController controller = loader.getController(); // fx:controllerに紐付いたコントローラインスタンスが得られる  

primaryStage.setTitle("Hello");  

// シーン生成  
Scene scene = new Scene(root);  
primaryStage.setScene(scene);  
primaryStage.show();  
} catch(Exception e) {  
e.printStackTrace();  
}  
instance = this;  
}  

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

/**  
* Page1へ遷移する  
* @param labelText  
*/  
public void sendhelloController(String labelText) {  

stage.setTitle("Hello");  

/*HelloController controller = new HelloController();  
this.replaceSceneContent(controller);*/  
}  

/**  
* シーンの変更  
* @param controller   
*/  
private void replaceSceneContent(Parent controller) {  
Scene scene = stage.getScene();  
if (scene == null) {  
scene = new Scene(controller);  
stage.setScene(scene);  
} else {  
stage.getScene().setRoot(controller);  
}  
}  

public void MainController() {  
//stage.setTitle("Page2");  
try {  
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));  
Parent root = loader.load();  
HelloController controller = loader.getController();   
stage2.setTitle("Hello");  

// シーン生成  
Scene scene = new Scene(root);  
stage2.setScene(scene);  
stage2.show();  
} catch(Exception e) {  
e.printStackTrace();  
}  

}  
public static Original getInstance() {  
return instance;  
}  

}  

HelloController.java

package fx;  

<略>  

public class HelloController extends AnchorPane{  

@FXML private Button helloButton;  
@FXML private Label  helloLabel;  

private int cnt;  

@FXML  
public void onHelloButtonClicked(ActionEvent event) {  

Original.getInstance().MainController();  
}  

public void onClick(ActionEvent event) {  

}  
}  

hello.fxml

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

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx.HelloController">  
<children>  
<Label fx:id="helloLabel" layoutX="156.0" layoutY="158.0" text="Hello World!!" textFill="#da0606">  
<font>  
<Font size="55.0" />  
</font>  
</Label>  
<Button fx:id="helloButton" layoutX="216.0" layoutY="72.0" mnemonicParsing="false" onAction="#onHelloButtonClicked" text="Button">  
<font>  
<Font size="34.0" />  
</font>  
</Button>  
</children>  
</AnchorPane>  

Main.fxml

<?xml version="1.0" encoding="UTF-8"?>  
<>  
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx.HelloController">  
<children>  
<VBox layoutX="127.0" layoutY="35.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">  
<children>  
<省略>  
</children>  
</GridPane>  
</children>  
</VBox>  
</children>  
</AnchorPane>  

試したこと

https://teratail.com/questions/97665 こちらの質問や https://qiita.com/mix/items/7c797eccad0a5e3a43c3 http://javafx-trick.appspot.com/article/110001/110009/80051.html これらのサイトを見ながらやったですが、JavaFX自体をイマイチ理解しておらず、 fxml上でcontrollerのIDを指定するやり方でやったほうがいいと記載されていたので、何とかそっちでやろうとしていますが丸2日奮闘しても対策方法が見つけることが出来ていません。 Controllerなどの引数のLabelTextとはコピペの名残で使用はしていません。 instanceも何に使っているのかわかってないです。 ゆくゆくは、postgresqlを使って会員サイトのようなものを作りたいのですが、javaプログラム上でcontrollerのIDを指定するのと、fxml上で指定するのどちらが良いかも教えていただけたら幸いです。 初めての質問なので欠けている項目などありましたら、ご指摘承りたく存じます。何卒よろしくお願いします

補足情報(言語/FW/ツール等のバージョンなど)

java version "1.8.0_151" eclipse:Version: Oxygen.1a Release (4.7.1a)

追記

MainController.java

package fx;  

import <略>  

public class MainController extends AnchorPane implements Initializable{  

@FXML  
private ChoiceBox<String> dropDown;  

@FXML  
private TextField textBox;  

@FXML  
private Button button;  

@Override  
public void initialize(URL location, ResourceBundle resources) {  
// 何もしない  
}  

public MainController() {  

}  


@FXML  
public void onClick(ActionEvent event) {  
// テキストボックスに文字列をセットする  
textBox.setText("ボタンを押しました。");  
}  

}  

MainConrollerへの切り替え Hellocontroller.java

package fx;  

import java.io.IOException;  
import java.net.URL;  
import java.util.ResourceBundle;  

import javafx.event.ActionEvent;  
import javafx.fxml.FXML;  
import javafx.fxml.FXMLLoader;  
import javafx.fxml.Initializable;  
import javafx.scene.control.Button;  
import javafx.scene.control.Label;  
import javafx.scene.layout.AnchorPane;  

public class HelloController extends AnchorPane{  

@FXML private Button helloButton;  
@FXML private Label  helloLabel;  

private int cnt;  


@FXML  
public void onHelloButtonClicked(ActionEvent event) throws IOException {  
/*this.helloLabel.setText("clicked! : " + cnt);  
cnt++;*/  
Original.getInstance().MainController();  
}  

public void onClick(ActionEvent event) {  
/*this.helloLabel.setText("clicked! : " + cnt);  
cnt++;*/  
//Original.getInstance().MainController();  
}  
}  

Original.java

package fx;  

import java.io.IOException;  

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

public class Original extends Application {  

private static Original instance;  

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

// インスタンス  
instance = this;  

try {  
FXMLLoader loader = new FXMLLoader(getClass().getResource("hello.fxml"));  
Parent root = loader.load(); // fx:rootに紐付いたノードインスタンスが得られる  
stage = primaryStage;  
// タイトルセット  
primaryStage.setTitle("Hello");  

// シーン生成  
Scene scene = new Scene(root);  

//scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());  
primaryStage.setScene(scene);  
primaryStage.show();  
} catch(Exception e) {  
e.printStackTrace();  
}  



}  

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

public void sendhelloController(String labelText) {  

stage.setTitle("Hello");  

HelloController controller = new HelloController();  
this.replaceSceneContent(controller);  
}  

private void replaceSceneContent(Parent controller) {  
Scene scene = stage.getScene();  
if (scene == null) {  
scene = new Scene(controller);  
stage.setScene(scene);//このステージで使用されるシーンを指定  
System.out.println("a");//確認用文字出力  
} else {  
stage.getScene().setRoot(controller);  

}  
}  

public void MainController() throws IOException {  
stage.setTitle("Main");  
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));  
Parent root = loader.load(); // fx:rootに紐付いたノードインスタンスが得られる  
MainController controller = loader.getController(); // fx:controllerに紐付いたコントローラインスタンスが得られる  
this.replaceSceneContent(controller);  

}  

public static Original getInstance() {  
return instance;  
}  

}  

Main.fxml

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

<?import javafx.scene.control.*?>  
<?import javafx.scene.text.*?>  
<?import java.lang.*?>  
<?import javafx.scene.layout.*?>  
<?import javafx.collections.FXCollections ?>  



<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx.MainController">  
<>  
</AnchorPane>  
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • masaya_ohashi

    2017/12/19 10:08

    stageとstage2で変数がわかれていますが、ウィンドウは2つ表示する予定ですか?

    キャンセル

  • hositaka

    2017/12/19 13:32

    ウィンドウは1つだけ表示する予定です。

    キャンセル

回答 1

checkベストアンサー

+1

 エラー箇所

stage2.setTitle("Hello");


stage2という変数はここに至るまで一なにも代入されていないためnullな状態です。
Main.fxmlを「別ウィンドウ」として表示するのであればstageとstage2を分ける必要がありますが、画面が切り替わるだけであればstageを流用しなければなりません。そもそもstart()の中でprimaryStageをstageへ代入する処理もないので、stageもnullですね。そこも直さないといけません。

 replaceSceneContentが用意されているのに使っていない

コメントアウトしている様子から、おそらくエラーが出たので自分なりにstart()の中をコピペしてMainController()のシーン生成部分を作られたのでしょうが、「JavaFXを理解していない」と自分でわかっていながら「アレンジ」を加えるのはよくありません。躓いたからといってお手本から逃げることは余計に悪化する一方です。料理初心者がレシピを守らずアレンジを加えて悲惨になることは容易に想像がつきます。
ただ、これは「ウィンドウは一つで中身が切り替わる画面遷移」の処理なので、ウィンドウが2つである場合、この処理を使っていないのは正解です。

 やりたいことが違うのにコントローラが1つしかない

JavaFXで、fxmlとcontrollerは対のものであり、hello.fxmlとMain.fxmlどちらもHelloControllerに紐付いているのはおかしいです。「レイアウトが違うだけで要素は同じ」である場合、このようなことも有りえますが、おそらくhelloButtonやhelloLabelがあるのはhello.fxmlだけであり、Main.fxmlにはないですよね?なのにMain.fxmlがHelloControllerに紐付いているのはおかしいです。MainControllerといった具合に、Main.fxml用のコントローラを専用に用意するべきです。Main.fxmlのfx:controllerにはそのMainControllerにしてください。

 javaプログラム上でcontrollerのIDを指定するのと、fxml上で指定するのどちらが良いか

あなたが参考にしたteratailの質問でも私が言っているように、fxml上でコントローラを指定することを推奨しています。fxml側でコントローラを指定しておけば、

 全体

コードを見るに、あなたはまだJavaの知識すら初歩段階であるにもかかわらず、JavaFXの知識ばかりを身に着けようとし、3ステップくらい先のことをやろうとしています。これではいつまでも理解が追いつかないままで進んでしまいます。私はこういったことをしてしまう初心者には厳しくあたって方向修正するようにしています。

 追記

不要コードとおぼしきものの削除、インデントの整理、処理の共通化を行いました。

Original.java

public class Original extends Application {
    private static Original instance;
    private Stage stage;
    @Override
    public void start(Stage primaryStage) throws Exception {
        // インスタンス
        instance = this;
        stage = primaryStage;
        try {
            // replaceSceneContentを改善してFXMLLoaderの処理の内包、
            // ウィンドウタイトルの変更も同時に行うよう修正。
            replaceSceneContent("hello.fxml", "Hello");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

    private void replaceSceneContent(String fxmlFile, String windowTitle) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlFile));
        Parent root = loader.load(); // fxmlに定義したノードの一番根っこの部分(hello.fxml、Main.fxmlで言えばAnchorPane)が得られる

        Scene scene = stage.getScene();
        if (scene == null) {
            scene = new Scene(root); // シーンの初期化に必要なのはノード
            stage.setScene(scene);//このステージで使用されるシーンを指定
                        stage.show();  
            System.out.println("a");//確認用文字出力
        } else {
            stage.getScene().setRoot(root);
        }

        // タイトルセット
        stage.setTitle(windowTitle);
    }

    public void MainController() throws IOException {
        this.replaceSceneContent("Main.fxml", "Main");
    }

    public static Original getInstance() {
        return instance;
    }
}

MainController.java

package fx;

import <略>

public class MainController implements Initializable { // extends AnchorPaneをやめる。コントローラとノードは別であるべき。
    @FXML
    private ChoiceBox<String> dropDown;
    @FXML
    private TextField textBox;
    @FXML
    private Button button;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // 何もしない
    }

    public MainController() {
    }

    @FXML
    public void onClick(ActionEvent event) {
        // テキストボックスに文字列をセットする
        textBox.setText("ボタンを押しました。");
    }
}

HelloController.java

package fx;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;

public class HelloController implements Initializable {// extends AnchorPaneをやめる。コントローラとノードは別であるべき。コントローラクラスにはInitializableインタフェースを実装しておくべき。
    @FXML
    private Button helloButton;
    @FXML
    private Label  helloLabel;
    private int cnt;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // 何もしない
    }

    @FXML
    public void onHelloButtonClicked(ActionEvent event) throws IOException {
        /*this.helloLabel.setText("clicked! : " + cnt);
        cnt++;*/
        Original.getInstance().MainController();
    }
}

hello.fxml、Main.fxmlに変更はなし。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/19 18:27

    ①例え話ですが、Stageは文字通り舞台のステージで、Sceneは背景の書き割りだと思ってください。
    Stageに設定されているSceneを取得するのがgetSceneです。初期段階では何のSceneも設定されていないので、nullが返ります。「あ、まだScene設定してないのね」とわかったら、渡されたrootを元にSceneを作ってStageに設定します。②に繋がることですが、「すでにSceneがあったら、土台を再利用して見た目だけ変えよう」というのがgetScene().setRoot()です。
    ③それはもちろんいつかは完成すると思いますが、次はpostgresqlをJavaで使うためにJDBCという技術が必要になります。それの勉強もまた大きな課題です。
    ④Java8の時点でOracleはSwingのサポート(というよりJavaFX以外のUI系のサポート)を打ち切ると言っています。まあ、かといってJavaFXが今後主流になるかと言われると、Oracleが早速放置し始めたのでなんとも言えないです。ですが、一応メインストリームはJavaFXのほうをやっていたほうがいいでしょう。
    ⑤関連ワードをとにかく拾い上げて検索します。エラーメッセージで検索するのはもちろん、使おうとしている技術の「入門」の資料を読み漁ります。

    キャンセル

  • 2017/12/19 18:41

    わざわざご丁寧に回答していただきありがとうございます。javafx-postgresqlで検索しても、あまりヒットせず戦慄していますが、やれるところまではやってみます。また、すぐにお世話になるかもしれませんが、何卒よろしくお願いします。質問者様に足を向けては寝られません。丸一日付き合っていただき本当にありがとうございました。

    キャンセル

  • 2017/12/20 09:06

    javafxとpostgresqlに直接的な関係はありません。JavaFXはデータを画面に表示するもの、PostgreSQLはDBからデータを引き出すもの、JDBCはJavaでDBにアクセスするためのもの、と役割が一つずつ分離しています。なので、個々の勉強をすれば自ずと組み立て方はわかるものです。ゆっくり一つずつ使いこなせるようになりましょう。

    あと、⑤の追加回答ですが、まずはエラーのスタックトレースを舐めるように見ることです。一番上には主因たる内容がきちんと書かれていますし、次の行以下には「どこで呼び出したメソッドで問題が起きたか」等の位置まで確認できます。はっきり言ってミスの8割はエラーメッセージとスタックトレースを見るだけで解決できるはずです。複雑な要因になってくると、ソースコードを深く追っていくことになりますが、それもきちんとめげずにやれば問題は9割5分は解決します。Eclipse等のデバッガを使えば、変数がどのように変わっていくかが見えるようになるので、それらのデバッグツールを使うのも有効です。デバッガが使えないなら、ログ出力等を埋め込んで「本来ここではこうなっているべきという想定が正しいか」を検証しながら問題箇所を探します。とにかくデバッグは地道な作業です。基本的に「バグ」と呼ばれるものの殆どは「コーディングした自分がどこかで使い方を間違えている」ことに起因します。言語やライブラリを疑う前に、自分を疑う癖を付けましょう。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる