ここから本文です

JavaFXでの、画面遷移のやり方について質問です。 http://oboe2uran.hatenablog...

tan********さん

2017/9/1417:18:26

JavaFXでの、画面遷移のやり方について質問です。

http://oboe2uran.hatenablog.com/entry/2017/03/05/170338
このサイトに書いてあるJavaFXでの画面遷移のプログラムで、幾つか分からない

部分があります。

まず第一に、MainAppクラスのsetPageメソッド内の、
BasePage page = (BasePage)loader.getController();
という部分です。
恐らくこの部分では、BasePageクラスのオブジェクトpageを作っているのだと思います。
しかし、その下に書いてあるBasePageの説明を見ると、BasePageは抽象クラスとなっています。
抽象クラスであるBasePageのオブジェクトを作成する事は、不可能なのではなかったのでしょうか?

次に、同メソッド内の、
Scene scene = new Scene((Parent)loader.load(cls.getResourceAsStream(fxml)), ~
という部分です。
分かり難かったので、この一行を以下の三行に分けました。
InputStream inputStream=cls.getResourceAsStream(fxml);
Parent root=fxmlLoader.load(inputStream);
Scene scene=new Scene(root,WINDOW_WIDTH,WINDOW_HEIGHT);
この「cls.getResourceAsStream(fxml);」というのが、何をやっているのか良く分かりません。
「cls」というのは、新しく作るページに対応した、そのページを動かす為のクラス(コントローラークラス?)という事でしょうか?
「getResourceAsStream」というのも良く分からず、調べた所「設定ファイルを読み込む」等と出て来たのですが、それが何故この場面で必要となっているのか等も分かりません。

次に、抽象クラスであるBasePageについてです。
このBasePageクラスの中には、抽象メソッドが一つも無い様に思います。
抽象クラスには、抽象メソッドは必ずある筈なのではなかったのでしょうか?

全体としても、どういった流れでこういったプログラムになっているのか良く分かりません。
このサイトに書いてある画面遷移のプログラムについて、一部でも全体でも構わないので、誰か説明して頂く事は出来ないでしょうか。

閲覧数:
346
回答数:
1
お礼:
500枚

違反報告

ベストアンサーに選ばれた回答

mdw********さん

2017/9/1621:34:44

BasePageに関してですが、抽象クラスには抽象メソッドを宣言できますが、必須ではありません。

Javaの標準APIではjava.awt.event.MouseAdapterなどがあります。


BasePage page = (BasePage)loader.getController();

fxmlにはコントローラとしてStartPageやAlphaPageなどのBasePageのサブクラスが設定されおり、FXMLLoaderはloadメソッド実行時にそれらのインスタンスを生成して、Object型フィールドとして保持しています。

getControllerメソッドはそのObject型のコントローラオブジェクトを型推論によりキャストして返すメソッドです。

上のコードの場合はBasePage型の変数pageに代入しようとしているのでBasePage型にキャストして返します。(よってキャスト演算子の(BasePage)の部分は不要です)

----------

cls.getResourceAsStream(fxml);

Class#getResourceAsStreamはそのクラス(clsのクラスのclassファイル)をロードしたクラスローダに引数のファイルの検索とInputStreamの生成を委譲し、そのInputStreamを返します。

つまりコントローラクラスと同じパッケージにfxmlファイルを配置しておけば、コントローラクラスを見つけたクラスローダには必ず見つける事ができるというわけです。

利点はアプリケーションをjarファイルにまとめた場合などにそのまま動作する点です。

OS上のパスでリソースを指定して読み込む場合はjarにした際にリソースをjarファイルとは別に用意する必要が出てきます。

Parent root=fxmlLoader.load(inputStream);

このコードのFXMLLoader#load(InputStream)ではInputStreamから読み込んだfxmlファイルの内容を解析して定義されているコントロールを生成し、それらをすべて含んでいるルート要素(例えばBorderPaneなど)をParent型で返しています。

----------

>JavaFXでの、画面遷移のやり方について質問です。

私もJavaFXの画面遷移について考え、サンプルを作ってみました。

採用をご検討ください。

アプリケーションは最初の画面で文字を入力してボタンを押下すると次の画面で文字列を逆さにして表示するだけです。


構成

TestApp Applicationのサブクラス
reverse.fxml 最初の画面
ReverseController 最初の画面のコントローラ(TestAppの内部クラス)
result.fxml 結果表示画面
ResultController 結果表示画面のコントローラ
Controller コントローラが実装するインタフェース
ReverseModel 文字データ保持と文字を逆さにするロジックを実装したモデル


構成の要点

画面の表示に必要なデータは一つまたは複数のモデルが保持する。

モデルはデータとそれを操作するメソッドを持つ。

コントローラは直接データを保持せず、モデルに用意されたメソッドを利用してイベント処理を行う。

モデルは例えばアプリケーションをSwingや3Dを使ったものに作り直す際にも、何も変更する必要がないように、アプリケーションクラスなどに依存しないように設計する。


改善点

モデルを使うパターンは目新しいものではありませんが、それと画面ごとのコントローラをアプリケーションクラスの内部クラスとして作成するのが今回考えたアイデアで、それ以外の部分は実際にアプリケーションを作成する中で改善していく必要があると思います。

特にControllerというインタフェースは取りあえず動くように用意したものなので、実際のアプリケーションではいろいろ機能を加えていく必要が出てくるような気がします。


以下はソースですがTestApp.java Controller.java ReverseModel.java reverse.fxml result.fxml を同じディレクトリに配置して以下のコマンドで動作します。

javac *.java
java TestApp

何か不明な点などあれば返信にてお願いします。


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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>


<BorderPane xmlns="javafx.com/javafx/8.0.102" xmlns:fx="javafx.com/fxml/1">
<center>
<TextField fx:id="textField" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</TextField>
</center>
<right>
<Button onAction="#reverse" text="Reverse" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</Button>
</right>
</BorderPane>


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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>


<BorderPane xmlns="javafx.com/javafx/8.0.102" xmlns:fx="javafx.com/fxml/1">
<center>
<Label fx:id="label" text="" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</Label>
</center>
<right>
<Button onAction="#back" text="Back" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</Button>
</right>
</BorderPane>


---------- Controller.java ----------
public interface Controller {
void updateModel(); //画面をもとにモデルを更新する
void updateView(); //モデルをもとに画面を更新する
}

---------- ReverseModel.java ----------
public class ReverseModel {
private String text = "";

public String getText() {
return text;
}

public void setText(String text) {
this.text = (text == null) ? "" : text;
}

public String reverse() {
if (text.isEmpty()) {
return "";
}
return new StringBuilder(text).reverse().toString();
}
}

---------- TestApp.java ---------
import java.util.HashMap;
import java.util.Map;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class TestApp extends Application {

private Parent reverseParent;
private Parent resultParent;
private ReverseModel reverseModel = new ReverseModel();
private Scene scene;
private final Map<Parent, Controller> controllerMap = new HashMap<>();

@Override
public void start(Stage primaryStage) throws Exception {
createAllParents();
scene = new Scene(reverseParent, 300, 100);
primaryStage.setScene(scene);
primaryStage.show();
}

private void createAllParents() throws Exception {
reverseParent = createParent("reverse.fxml", new ReverseController());
resultParent = createParent("result.fxml", new ResultController());
}

private Parent createParent(String fxml, Controller controller)
throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource(fxml));
fxmlLoader.setController(controller);
Parent parent = fxmlLoader.load();
controllerMap.put(parent, controller);
return parent;
}

private void changeScene(Parent parent) {
Controller controller = controllerMap.get(parent);
controller.updateView();
scene.setRoot(parent);
}

private class ReverseController implements Controller {
@FXML
private TextField textField;

@Override
public void updateModel() {
reverseModel.setText(textField.getText());
}

@Override
public void updateView() {
textField.setText(reverseModel.getText());
}

@FXML
public void reverse(ActionEvent e) {
updateModel();
changeScene(resultParent);
}
}

private class ResultController implements Controller {
@FXML
private Label label;

@Override
public void updateModel() {
//do nothing
}

@Override
public void updateView() {
label.setText(reverseModel.reverse());
}

@FXML
public void back(ActionEvent e) {
reverseModel.setText("");
changeScene(reverseParent);
}
}

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

  • mdw********さん

    2017/9/1621:36:32

    一つ前のご質問で統合開発環境を利用されていないとありますが、mainメソッドだけで済むような簡単なプログラムならテキストエディタでも十分ですがアプリケーションやライブラリなどを作るのであればIDEがあった方が良いと思います。

    IDEが初めての場合は取りあえず最初から日本語化されており、エラーや警告のメッセージも分かり易いのでNetBeansをお薦めします。

    操作などは特に学習する必要はないと思います。

    プロジェクトやクラスなどを右クリックで作成する方法とmainのあるクラスを右クリックして実行する方法だけ覚えて、他の操作はおいおい覚えていけば十分です。

    またfxmlを使う場合にはScene Builderを使って画面を作成する場合がほとんどだと思います。

    Gluonのサイトにバイナリがあるのでインストールされたら良いかと思います。

    もしも試してみる気になった際にインストールやインストール後の設定、操作に関して何か不明な点があれば返信にてどうぞ。

  • その他の返信を表示

返信を取り消しますが
よろしいですか?

  • 取り消す
  • キャンセル

質問した人からのコメント

2017/9/21 09:29:27

分かりました。
NetBeans…入れてみようかな…

投票になってしまいそうなので、この質問終わらせます。

みんなで作る知恵袋 悩みや疑問、なんでも気軽にきいちゃおう!

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問は選択されたID/ニックネームのMy知恵袋で確認できます。

不適切な投稿でないことを報告しました。

閉じる