torutkのブログ

ソフトウェア・エンジニアのブログ

JavaFXのTableViewをScene Builderで作成

JavaFXのTableViewは、SwingのJTableとは随分と景色が違って、データの表示のさせ方でさ迷ってしまいました。

いくつかあるTableViewのサンプルは、Javaコードで記述したものか、FXMLを手で作成しているもので、Scene Builderで作る場合のずばりの記述がなかなか見当たりませんでした。

Scene Builderの「ライブラリ」(GUI部品が並んでいるパレット)にTable Viewがあるのでこれをデザイン画面にドラッグ&ドロップします。デフォルトでは2つの列が生成されます。

あとは、この表にデータを入れるコードの書き方ですが、ここから悩みが始まりました。

TableViewのデータ管理は、SwingのJTableのようにTableModelでデータと列(カラム)の対応を制御するの違い、データはTableView.getItems()で参照できるObservableListで保持し、列(カラム)との対応は、列ごとに生成するTableColumnオブジェクトとデータのクラスのプロパティを紐づける感じです。

なんとなく理解したイメージをクラス図っぽく書いてみました。

+-----------+       +-------------+
| TableView |◆-----| TableColumn |
+-----------+       +-------------+
      ◇                      |
      |items                 | 
+-----+------------+     +-------+
| ObservableList<T> |◇---|  T    |
+-------------------+     +-------+

ここで、TableColumnオブジェクトとTオブジェクト(のプロパティの1つ)を結び付ける方法が課題となります。

OracleJavaFXドキュメント「Buil UI with FXML」

を見ると、FXML側で次のように記述しています。

  <TableColumn text="First Name">
      <cellValueFactory>
          <PropertyValueFactory property="firstName" />
      </cellValueFactory>
  </TableColumn>

Scene Builderで、TableColumnを選択し、プロパティやレイアウトなどを見ても、cellValueFactory、PropertyValueFactory的な設定をする場所が見当たりませんでした。

そこで、SceneBuilderでFXML的に定義する方法を追及するのはやめて、Scene Builderで配置したTableColumnを、コントローラクラスで取得し、コード側でcellValueFactoryを設定してみました。

public class CharCodeViewController implements Initializable {
    @FXML
    private TableView<Person> table;
    @FXML
    private TableColumn<Person, String> nameColumn;
    : (中略)
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        encodeColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
    }
}

NetBeansJavaFX FXMLアプリケーションで作成すると、fxmlファイルとコントローラクラスの雛形が生成されるので、このコントローラクラスに上述のようにScene Builderで配置したTableViewとTableColumnのインスタンスを追記します。
そして、initialize()メソッドの中で、JavaAPIでデータ(CharCodeData)のプロパティと対応付けています。

一応こんなイメージで表示されるようにはなりましたが、Scene BuilerでTableViewを使う方法として適しているかは分かりません。

Scene Builderを使ってTableViewを配置するときは、もうちょっとScene Builder側で定義する方法があるといいなぁと思います。

TableViewサンプル作成でやった手順をずらずらと

ここからは、試行錯誤でTableViewを使った画面をScene Builderで作った際の手順をずらずら書いた、忘備メモです。

NetBeansでプロジェクト作成

NetBeansで[ファイル]>[新規プロジェクト]で[JavaFX FXMLアプリケーション]を指定、プロジェクト名に[HelloTable]、FXML名に[HelloTable]を記入し[終了]ボタンを押す。

Scene Builderで画面配置

生成されたHelloTable.fxmlをプロジェクトツリー上でダブルクリックするとJavaFX Scene Builderが立ち上がる。

デフォルトで貼られているButtonを削除、TableViewを画面に貼る。この状態で階層を見ると、次のようになっている。

 AnchorPane
   +-- Label
   +-- TableView
         +-- TableColumn  列X
         +-- TableColumn  列X

TableColumnのプロパティTextに、デフォルトで設定されている列Xを、名前、および年齢に変更する。

コントローラからGUIコントロールにアクセスするため、TableViewと各TableColumnにfx:idを設定する。

配置したコントロール 付与したfx:id
TableView table
TableColumn 名前 nameColumn
TableColumn 年齢 ageColumn

ここで、Scene Builderのfx:id欄に[▼]があるが、これを押してもリストには[null]のみ出てくる。キー入力でnameColumnと入れると、入ることには入るが、次の警告がでる。

ID nameColumnのFXMLコントローラ・クラスに注入可能なフィールドが見つかりません

コントローラクラスに@FXMLフィールド作成

そこで、先にコントローラクラスHelloTableControllerにフィールドを定義する。

public class HelloTableController implements Initializable {

    @FXML TableView table;
    @FXML TableColumn nameColumn;
    @FXML TableColumn ageColumn;
    : (後略)

すると、Scene Builderのfx:id欄の[▼]を押すと、候補リストが表示される。

データクラス作成

テーブルに表示するデータを表すクラスを作成する。

package hellotable;

public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
}
  • (2013-01-09追記)この定義では後日列データだけ変更したときに自動でTableView表示に反映されません。詳しくは次の日記を参照。
TableColumnとPersonクラスの対応付け

コントローラクラスのinitializeメソッドに対応付コードを記述する。

    public void initialize(URL url, ResourceBundle rb) {
        nameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
        ageColumn.setCellValueFactory(new PropertyValueFactory<Person, Integer>("age"));

        // サンプルデータを1件
        table.getItems().add(new Person("Adam Smith", 32));
    }
表の文字の指定

最初、Scene Builder上から、TableColumnのプロパティでCSSのstyleにフォント指定をしたが、プログラムを実行すると一瞬フォントが変わってすぐに戻る状態になった。

TableViewのプロパティでCSSのstyleにフォント指定をすると、表全体になるがフォントが適用された。

-fx-font: BOLD 16 monospaced;

フォントの指定についてもよく分からないが、フォント種類、サイズを指定する場合、このように記述するらしい。

表の列幅の指定

列幅は、Scene BuilderのTableColumnでレイアウトのPref Widthを変更する。
Widthは変更不可な属性だったので最初悩んだが、Pref Widthを変更するとWidthも同じ値に変更され、レイアウト画面上の幅も変更になる。