2013/5現在 Natbeansは7.2.1と7.3を使って,JavaFX Scene Builder は1.1を使って開発実験してみる。
JavaFXはSwingと違って,GUIマネージャーが,Natbeansから分離しScene Builder単体となってしまったが,
Eclipse勢のことも考えてそうしたのだろう,なんか軽くて良い感じがする,デザインもかなり変化してきた,
大きく違うのは,GUIのデザインに部分をXML形式にして,HTMLなどによく使われるCSSなどのデザイン形式に,
した点だろう,最初はSwingとの違いに戸惑うが,パターンがわかれば慣れてくるだろうなあ。
これは仕様なのかバグなのか分かりにくいがButtonイベントを実装しようとすると,エラーが出る,
やり方はButtonイベントをXMLファイルの方から書いてコンパイルして実行するとエラーが起きにくい,
Scene BuilderのOnActionに先に書き込むとダメ?みたい,お作法がまだわかってないかも。
どうやらメソッド名かファイル名を間違えていただけだった,確認し無いとダメだなあOrz,てかIDEの
デバッグで「ファイル名が違うよ」とか「メソッド名が違うんじゃない?」とかアドバイス出来ないもんかなあ,
訳わからん赤文字のエラーがズラッと並んでも分かりにくいわあ,コンパイルする前段階でチェックする機能を
実装してほしいねえ。
プロジェクト名を後から変更した場合project.propertiesの中のプロジェクト名を手動で変更しないとダメみたい,
JavaFXの場合必要なファイルは3つ,Mainメソッドが書かれてる.javaファイルと,GUIの設計書みたいな事が書かれてるFXMLファイルと
FXMLファイルに書かれてるcontrolをコントロールする.javaファイルが重要で,デザインを別ファイルにしたい場合はCSSファイルを作る,
後はプロジェクトによって外部クラス用のjavaファイルを増やすことになるが,基本的には3つのファイルで動作可能。
JavaFX用のインポートライブラリとSwing用のインポートライブラリを間違えてはイケナイ!!
これはIDE側で「必要なライブラリを入れますか?」という表示が出るのだが,この時に間違えてSwing用の
ライブラリーを入れてもコンパイルが通ってしまうが,実行時エラーになるので確認が必要である。
ちなみにjavafxなんちゃらってのが出れば迷わずそれを入れる。
SwingにはあったJDialogがJavaFXには何故か無くなってる?必要ないという判断か?
しかしそれでは自分的には困るので,実装方法を考えてみた。
なるべく簡略化出来るようにしてあるつもりだが,他にいい方法が現状分からない。
まず必要なファイルを3つMainメソッド用.javaファイル,FXMLファイル,controlをコントロールする.javaファイルを作り,
そのcontrolファイルからDialog用のFXMLファイルとcontrolをコントロールする.javaファイルの2つのファイルを追加する。
Mainのコントロールするファイル内部に以下のように書く
---package 省略--- ---import 省略--- ---class 省略---
public void dialogset(ActionEvent dialog) throws IOException{
Parent root2 =FXMLLoader.load(getClass().getResource("dialog.fxml"));
Scene scene2 = new Scene(root2);
Stage DialogStage = new Stage(StageStyle.UTILITY);
DialogStage.setTitle("Sample_dialog");
DialogStage.setScene(scene2);
DialogStage.show();
}
---initialize 省略---
変数名は適当で,ダイアログを呼び出すcontrolコンポーネントにイベントハンドラを割付し,
親ルートを宣言,シーンにルート設定し,ステージのスタイルを設定,
タイトル設定,ステージにシーンを設定,ステージを表示という感じ。
これでメインのフレームからボタンなりMenuItemなどからダイアログを呼び出せるようにはなった。
JavaFX8以降でDialogコンポーネント復活でした
スレッド内からラベルに表示させようとすると,なぜか表示されない。
よってTextFieldかTextAreaに表示させなければ,いけないみたい。
Swingだと表示用のコンポーネントにアクセス出来るんだけどなあ。
JavaFX1.1のb11からb15にバージョンUPしようとして、b11をアンインストールして、
b15をインストールしようとしたら、このバージョンはすでにインストールされています。
という警告が出てインストール出来ない、よってその時はレジストリに残っている情報を、
削除するしかない、が手動は危険なのでツールを使う方がいい、
自分はRevo Uninstallerを使ってアンインストールした。
その後NetBeansを起動するとJavaFXの関連プロジェクトがエラー表示になるが、
プロジェクトのプロパティで、ライブラリのJavaプラットフォームでJavaFXを選択すればOK、
この時,JavaプラットフォームマネージャーでJavaFXの設定をしておかなければいけない、
無い場合、プラットフォームの追加で、JDK1.7の場所を指定してやればいい。
JavaFXはJava7からAPI内部に導入されたので先に作っておいたプロジェクトがある場合は
気をつけなくてはいけないポイントがある。
まずツールのJavaプラットフォームマネージャの中のプラットフォームに新規に
JavaFXの新バージョンを追加しておく、それから各プロジェクトのプロパティの中の
ライブラリの参照が壊れている物をJDK内部のライブラリに変更しする,
最後にビルドし直せば,赤くなったプロジェクトは黒くなるはず。
javaファイルで宣言したメソッドをScene Builder側でボタンなどに割りつけした時
メソッドの前に#が付いてしまいNetBeans側のfxmlファイルにエラー表示されるのだが、
これでいいみたい、#を外すと動作しないのでリンクはキッチリ出来てるみたいだ、なんだかなあ~。
2017/9現在 いつの間にやら修正されていたのでエラーは出ない
メソッドは必ずpublicにしないとダメのよう、Swingの時はprivateで宣言してたが、privateにすると
Scene Builderで読み込めないのでpublicにしないといけないようだ。
Swingから移植する時、イベントの引数メソッドの書き方が少し違うので注意。
Gloun製のScene Builderは調子が悪すぎなので
Oracle純正のScene Builderをいまだに使ってる
サイトからDLしてきて
アーカイブマネージャーで解凍して その中のフォルダを右クリックで展開押して
入れたいディレクトリを指定するだけ
2017/9現在 Javaの補完は日本語で出るのだがJavaFXの補完は英語で出るので
それを日本語化する方法を明記しておく
まずJavaFXリファレンスの日本語サイトでURLをコピーして
Eclipseの設定のJDKファイルを選択するところの内部に
jfxrt.jarファイルを展開すると内部にJavadocロケーションの設定がある
そこにURLをさっきの日本語リファレンスのを入れると日本語化できる
オフラインで使いたい場合 日本語サイトを丸ごとDLするツールで
落としてきて Zipファイルにすればいいのだが 一つ問題は
内部のHTMLファイルの文字コードがデフォではMS932になっているので
Eclipseの設定がMS932の人はそのままでもいいが
UTF-8の設定に変えている場合は文字化ける
そのため落としてきたサイトのHTMLファイルを一括で文字コード変換
してくれるツール等を使って変更しておく必要がある
2017/9現在 JavaFXのプロジェクトは FXMLファイルと Mainコントローラー.javaと Subコントローラー.java
の3つがEclepseやNetBeansではデフォで作られる Mainコントローラー.javaは基本何も触らない
Subコントローラー.javaには各コンポーネントの大まかなメソッドの宣言だけに留めておいて
コンポーネントごとの細かい処理は別のFuncコントローラー.javaに書き出す方がいい
がしかしfx:controller=の設定では基本的に一つのFXMLファイルには一つのSubコントローラー.javaが
対応することになっているのでFXMLファイルに複数のSubコントローラー.javaを割り当てることができない
よって一枚のFXMLファイル上のコンポーネントの処理を分けるにはSubコントローラー.javaから
Func.javaファイルを複数作って分ける方が良いと考えている
下に例を示す
Sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.SampleController">
<center>
<Pane prefHeight="317.0" prefWidth="330.0" BorderPane.alignment="CENTER">
<children>
<Button fx:id="b1" layoutX="132.0" layoutY="114.0" mnemonicParsing="false"
onAction="#ButtonAction" text="Button" />
<Label fx:id="l1" layoutX="90.0" layoutY="177.0" prefHeight="17.0"
prefWidth="136.0" text="Label" />
</children>
</Pane>
</center>
</BorderPane>
(Main.Java)Mainコントローラー.java
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass()
.getResource("Sample.fxml"));
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);
}
}
(SampleController.java)Subコントローラー.java
package application;
import javafx.event.*;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class SampleController {
@FXML public Button b1;
@FXML public Label l1;
@FXML
public void ButtonAction(ActionEvent ev) {
l1.setText("test");
}
}
上記はPaneに張り付けたButtonからLabelにtestという文字列を表示させているだけだが
このbutton内部の処理をFunc.javaファイルに分けてみる FXMLファイルとMainファイルは同じで
Subコントローラー.javaファイルの書き換えと Func.javaファイルを処理用に追加している
(SampleController.java)Subコントローラー.java
package application;
import javafx.event.*;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class SampleController {
@FXML public Button b1;
@FXML public Label l1;
Func func = new Func(); //←これがFunc.javaを呼び出しインスタンス化している
public String str = "";
@FXML
public void ButtonAction(ActionEvent ev) {
str = func.func(); //←これでFunc.javaのメソッドを呼んで 代入している
l1.setText(str);
}
}
Func.java
package application;
public class Func {
public String str = "asd";
public String func() {
return str;
}
}
上記のやり方でbutton部分の処理を別ファイルに切り分けだしている
これで各コンポーネント(Button等)ごとに内部の処理を別のjavaファイルに
個別に分けることが出来るので 複雑なアプリを組む時には重宝するやり方だと思う
上記は一番簡単な呼び出し方でやっているし同一パッケージ内なので
インポートが必要ないが別パッケージでやる場合はインポートした方がいい
混乱しやすい要因
①ネット上のJavaFX関係の情報はだいたいエントリーポイント内(メインクラスのファイル)に処理を書き込んでいる
②Netbeans自体がJavaFXのプロジェクトを作るとメインのJavaファイルとControllerファイル、
デザイン用のFXMLの3つが最初に自動で作られる これが一番ベスト?な作り方になるのかな
③Gloun製のScene Builderがバグがあるので使いずらい (プロパティがちゃんと見れない)
プロジェクトのプロパティー内の ライブラリ→実行のクラスパス指定で そのプロジェクト内の
distフォルダを指定追加しなければ以下のようなエラーが出る (間違えて消していたのでわかった)
dist folder need additional to Project execute property
C:\NetBeansProjects\JavaFXApplication\nbproject\jfx-impl.xml:3550: The following error occurred while executing this line:
C:\NetBeansProjects\JavaFXApplication\nbproject\jfx-impl.xml:3578: The following error occurred while executing this line:
C:\NetBeansProjects\JavaFXApplication\nbproject\jfx-impl.xml:3597: The following error occurred while executing this line:
C:\NetBeansProjects\JavaFXApplication\nbproject\jfx-impl.xml:1251: The following error occurred while executing this line:
C:\NetBeansProjects\JavaFXApplication\nbproject\jfx-impl.xml:1276: You must specify a path to convert
ビルド失敗(合計時間: 0秒)
This ploblem is Netbeans develop version only...
上記のエラーはNetBeansの開発バージョンだけ みたいでした><
すなおに安定版8.2を使えば 指定する必要無いみたい
まずNetbeansでJavaFXのFXMLファイル用のプロジェクトを作って
Scene BuilderにLineChartを張り付けて作った時につまずいたポイント
コントロールファイルに分けようとしたら表示されない・・・なぜかと調べてたら
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="580.0" prefWidth="620.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication2.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="14.0" layoutY="14.0" onAction="#handleButtonAction" text="Click Me!" />
<Label fx:id="label" layoutX="109.0" layoutY="18.0" minHeight="16" minWidth="69" prefHeight="17.0" prefWidth="185.0" />
<LineChart fx:id="linechart1" layoutX="106.0" layoutY="120.0">
<xAxis>
<NumberAxis side="BOTTOM" /> ←これがCategoryAxisになったままだと表示されない
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
</children>
</AnchorPane>
赤文字のとこはX軸が数値の時はNumberAxisで 文字列の時はCategoryAxisにしなくてはいけない
FXMLDocumentController.java
package javafxapplication2;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
public class FXMLDocumentController implements Initializable {
@FXML private Label label;
@FXML private NumberAxis naxsis ; ←これはこのソースでは必要無い
@FXML private LineChart<Number,Number> linechart1;
@FXML
private void handleButtonAction(ActionEvent event) {
// データを作成
Series series2 = new Series(); ←ジェネリクスは自明なので省略出来る
series2.setName( "日本" );
series2.getData().add( new Data( 1 , 12 ) );
series2.getData().add( new Data( 2 , 23 ) );
series2.getData().add( new Data( 3 , 34 ) );
series2.getData().add( new Data( 4 , 45 ) );
series2.getData().add( new Data( 5 , 56 ) );
// データを登録
linechart1.getData().addAll( series2);
}
@Override
public void initialize(URL url, ResourceBundle rb) {
Series series1 = new Series();
series1.setName( "日本" );
series1.getData().add( new Data( 1 , 12 ) );
series1.getData().add( new Data( 2 , 23 ) );
series1.getData().add( new Data( 3 , 34 ) );
series1.getData().add( new Data( 4 , 45 ) );
series1.getData().add( new Data( 5 , 56 ) );
// データを登録
linechart1.getData().add( series1 );
}
}
イニシャライズ内は起動後にすぐに描画
ボタンイベントは押した時に描画
JavaFXApplication2.java
package javafxapplication2;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXApplication2 extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}