JavaFX / TableViewを編集可能にする

## 環境

- JDK 1.8.0_131
- JavaFX Scene Builder 8.3.0

## 概要

テーブルビューはユーザ定義のクラス（データモデル）と行を対応付けるのが基本的な使い方です。このあたりの解説は以下を参考にすると分かりやすいです。

[AOEの日記: ListViewやTableViewのセルをカスタマイズする方法](http://aoe-tk.hatenablog.com/entry/20131206/1386345344)
[ORACLE: JavaFX UIコンポーネントの操作 13 表ビュー](https://docs.oracle.com/javase/jp/8/javafx/user-interface-tutorial/table-view.htm)


このエントリーではそのようにして作成されたテーブルビューを編集可能にする仕組みとそのバリエーションを調べてみます。

## サンプルコード

[Controller.java](https://github.com/monolith52/Snippets/blob/master/src/main/java/customtable/Controller.java)
[Entity.java](https://github.com/monolith52/Snippets/blob/master/src/main/java/customtable/Entity.java)
[Main.fxml](https://github.com/monolith52/Snippets/blob/master/src/main/resources/customtable/Main.fxml)

![0.png](https://qiita-image-store.s3.amazonaws.com/0/168976/d9ed1a2d-d0dc-1021-5cf9-c9163134c61c.png)


## テキスト編集可能なセル

`TextFieldTableCell`を使用するとセルをテキスト編集可能にできます。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/168976/00e44bc0-3db5-8c16-c428-ae388d0e7695.png)

`TextFieldTableCell`を使用するときは`TableColumn`の`CellFactory`をデフォルトから変更します。**`TableView`と`TableColumn`両方のeditableプロパティがtrueにされていなければ編集できない**ことに注意してください。(Scene Builderでeditableにしておくこともできます)

```java:Controller.java
tableView.setEditable(true);
column1.setEditable(true);
column1.setCellFactory(TextFieldTableCell.forTableColumn());
```
`TextFieldTableCell`は**編集状態のON/OFF**によって、表示するコンポーネントを`TextField`に切り替える仕組みになっています。

### StringConverter
StringConverterは文字列を他のクラスに相互変換するための仕組みを提供します。

プロパティの型がString以外の場合はこのStringConverterが必要になります。StringConverterには変換対象となる型ごとに多くの実装が用意されているためその中から選んで使用することになります。
[パッケージ javafx.util.converter](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/util/converter/package-summary.html)

```java
// Boolean型のカラムをテキスト編集する場合
column3.setCellFactory(TextFieldTableCell.forTableColumn(new BooleanStringConverter()));
```


## 選択編集可能なセル
`ChoiceBoxTableCell`,`ComboBoxTableCell`を使用するとセルを選択編集可能にできます。ChoiceBoxの方は選択肢にスクロール機能を提供しないので選択肢が多い場合には向いていないといった特徴があります。

ChoiceBox
![3_.png](https://qiita-image-store.s3.amazonaws.com/0/168976/c3231b5c-d4e0-a704-9cc8-c944c7b1d1d9.png)

ComboBox
![4.png](https://qiita-image-store.s3.amazonaws.com/0/168976/271fb49a-f061-a134-6d3d-4117385421dd.png)

```java:Controller.java
// ChoiceBox
column2.setCellFactory(ChoiceBoxTableCell.forTableColumn("Item1", "Item2", "Item3"));
// ComboBox
column2.setCellFactory(ComboBoxTableCell.forTableColumn("Item1", "Item2", "Item3"));
```

これらも同様に編集状態のON/OFFによって表示するコンポーネントを`ChoiceBox`,`ComboBox`に切り替える仕組みになっています。


## トグル編集可能なセル
`CheckBoxTableCell`を使用するとセルをトグル編集可能にできます。このセルは編集状態を持たず、シングルクリックで値を変更できます。

![5.png](https://qiita-image-store.s3.amazonaws.com/0/168976/7e405ff9-051a-e06a-4bcd-30d2c9e8f491.png)

```java:Controller.java
column3.setCellFactory(CheckBoxTableCell.forTableColumn(column3));
```

## 編集可能なカスタムセル
編集時にJavaFX標準の`ColorPicker`を表示するカスタムセルを作ってみます。非編集時には空Paneを塗りつぶしたものを置いています。

![6.png](https://qiita-image-store.s3.amazonaws.com/0/168976/6b7a773f-7977-8c63-3ce0-a9dfd3ef8d25.png)

[ColorPickerTableCell.java](https://github.com/monolith52/Snippets/blob/master/src/main/java/customtable/ColorPickerTableCell.java)

```java
column4.setCellFactory(ColorPickerTableCell.forTableColumn());
```

大雑把な遷移イメージは以下の通りです。`setGraphic`によって表示コントロールが切り替わるタイミングや`commitEdit`がカラーピッカーの`OnAction`内で呼ばれる仕組みが理解できれば他のコントロールでも応用できるかと思います。

![edit2.png](https://qiita-image-store.s3.amazonaws.com/0/168976/bd4b17b8-8c0a-ed14-69ac-60f205afeb3f.png)


## 編集状態を使わないカスタムセル
編集状態を使わずコントロールを直接配置する方法でもカラーピッカーを表示してみます。一般的にカスタムセルとして実装されるのはこちらのパターンの方が多いのではないでしょうか。上記のチェックボックスと同じような方法です。

![7.png](https://qiita-image-store.s3.amazonaws.com/0/168976/7ea4de83-ccf2-3224-9227-5efb38c10ce4.png)

[DirectColorPickerTableCell.java](https://github.com/monolith52/Snippets/blob/master/src/main/java/customtable/DirectColorPickerTableCell.java)

```java
column4.setCellFactory(DirectColorPickerTableCell.forTableColumn());
```

編集状態を使わないのでカラーピッカーの`OnAction`から直接データモデルの値を書き換えています。

仕組みがシンプルでユーザーに対しても編集できることが伝わりやすいのでよいのですが、画面内に重いコントロールが並ぶことによって負荷が高くなることに注意する必要があります。


## ビルトインセル一覧

最初に紹介したビルトインのセルはテーブル用だけでなくツリー用やリスト用などの様々な種類があります。カスタムセルを作る前に確認すると良いでしょう。
[パッケージ javafx.scene.control.cell](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/control/cell/package-summary.html)


## 参考
[軽Lab: JavaFX セルのカスタマイズ](http://krr.blog.shinobi.jp/javafx/javafx%20%E3%82%BB%E3%83%AB%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA)
[ソース: GitHub](https://github.com/monolith52/Snippets/tree/master/src/main/java/customtable)
[FXML: GitHub](https://github.com/monolith52/Snippets/tree/master/src/main/resources/customtable)