メモの日々


2014年02月05日(水) [長年日記]

[java] JavaFXで表

久しぶりにJavaFXをいじる。前回から1ヶ月経ってしまった。

JavaFX 2で始めるGUI開発 第5回 リスト、コンボボックス、テーブルを読んだ。テーブルが気になるので試してみた。

できあがり

画面イメージ

名前と値を表示するだけ。値の方は編集可能。

表はデフォルトで縞々になるみたい。行や列が無い部分にもセルが補われて表示されている。

以下ソースコード。

行に表示するクラス

SwingのJTableとは違い行ベースの表を簡単に作れるみたい。次のようなRowクラスを作った。

package table;

public class Row {
    private final String name;
    private int value;

    public Row(String n, int v) {
        name = n;
        value = v;
    }

    public String getName() { return name; }

    public int getValue() { return value; }

    public void setValue(int value) { this.value = value; }
}
  • nameは変更不可で、valueは変更可能にしている。

コントローラクラス

package table;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.IntegerStringConverter;

public class TableController implements Initializable {
    @FXML
    private TableView<Row> table;

    @FXML
    private TableColumn<Row, String> nameColumn;

    @FXML
    private TableColumn<Row, Integer> valueColumn;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        nameColumn.setCellValueFactory(
            new PropertyValueFactory<Row, String>("name"));
        valueColumn.setCellValueFactory(
            new PropertyValueFactory<Row, Integer>("value"));
        valueColumn.setCellFactory(
            TextFieldTableCell.<Row, Integer>forTableColumn(
                new IntegerStringConverter()));
    }

    public void setRowList(ObservableList<Row> rowList) {
        table.setItems(rowList);
    }

    @FXML
    public void handleValueCommeted(CellEditEvent<Row, Integer> event) {
        event.getRowValue().setValue(event.getNewValue());
    }
}
  • 表はTableView、列はTableColumnクラスで作れる。これらはFXMLの方で関連付ける。Rowクラスとの関連付けは型パラメータで行っている。
  • initialize()にて、Rowのメソッドと列を関連付けている。文字列ベース。表示用にはcellValueFactory、編集用にはcellFactoryを設定するという理解でよい?cellValueFactoryにはPropertyValueFactoryクラス、cellFactoryにはTextFieldTableCellが生成するクラス使える。まだよく分かっていないな。
  • 表に表示するRowオブジェクトをsetRowList()で設定するようにした。もっとスマートに設定したいけれど。
  • handleValueCommited()は、値列の編集が完了したときに呼ばれる(ようにFXMLを書く)。編集結果を使ってRow#setValue()を呼び出している。

FXML

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

<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="200.0" prefWidth="300.0"
            xmlns:fx="http://javafx.com/fxml/1"
            xmlns="http://javafx.com/javafx/2.2"
            fx:controller="table.TableController">
  <children>
    <ScrollPane fitToHeight="true" fitToWidth="true"
                AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0"
                AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
      <content>
        <TableView fx:id="table" editable="true" >
          <columns>
            <TableColumn minWidth="25" prefWidth="75"
                         text="名前" fx:id="nameColumn" />
            <TableColumn minWidth="25" prefWidth="75"
                         text="値" fx:id="valueColumn"
                         editable="true" onEditCommit="#handleValueCommeted" />
          </columns>
        </TableView>
      </content>
    </ScrollPane>
  </children>
</AnchorPane>
  • 表の外側にScrollPaneを置いてみた。
  • TableViewの中にTableColumnを置いている。
  • 列のタイトルを設定している。
  • 値列だけ編集可能にしている。

メインクラス

package table;

import java.io.IOException;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Table extends Application {
    private ObservableList<Row> rowList;

    @Override
    public void init() {
        rowList = FXCollections.observableArrayList(
            new Row("壱", 1),
            new Row("弐", 2),
            new Row("参", 3));
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        FXMLLoader loader =
            new FXMLLoader(getClass().getResource("Table.fxml"));
        loader.load();

        TableController controller = loader.getController();
        controller.setRowList(rowList);

        Parent root = loader.getRoot();
	primaryStage.setScene(new Scene(root));
	primaryStage.setTitle("Table");
	primaryStage.show();
    }

    public static void main(String[] args) {
	launch(args);
    }
}
  • init()をオーバーライドし、そこでRowオブジェクトを生成してみた。TableViewに登録するにはObservableListというListを使う必要があって、FXCollectionsクラスを使うと簡単に生成できる。
  • コントローラにRowオブジェクトを渡すため、FXMLLoaderインスタンスを明示的に生成している。FXMLLoader#getController()を使うとコントローラオブジェクトを取得できる。ジェネリックメソッドになっていてキャスト不要。