No Bugs, No Life

読んだ本や、プログラミング、システム開発等のねたを中心に。文章を書く練習なので少し硬派に書くつもりだけど、どうなることやら。

JavaFX:ListViewの基本的な使い方

JavaFXでListViewやTableViewを使う際には、ObservableListにデータを追加して内部のデータと画面上の表示をバインドすることになるけど、自分自身が割りと混乱しがちなので一度メモとして書き下しておく。
今回のエントリでは、ListViewについて。次回はTableViewの予定。

ListView

基本的な使い方(文字列のListView)

MWではGenerateConfirm/SelectMementoTypeで使用しているような、単純に文字列を描画するリスト(ListView)を使用する場合には単純にObservableListにデータを追加し、ObservableListをListViewにsetItems()すればOK(以下は関連箇所のみを抜粋)。

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ListView;

public class SelectMementoTypeController extends AnchorPane implements MWSceneController {
	// 画面項目
	@FXML private ListView<String> listView;
	private ObservableList<String> listRecords = FXCollections.observableArrayList();
	
	/**
	 * ListViewにメメントタイプ一覧を設定する
	 */
	private void fillTypeListView(){
		for(String category:processor.findMementoCategoryProcess()){
			logger.info("カテゴリ["+category+"]を追加します。");
			listRecords.add(category);
		}
		listView.setItems(listRecords);
	}
}

CellFactoryを用いる方法(カスタムクラスのリスト)

文字列リストとかならとてもシンプルなのだが、内部的に管理するデータはMementoのリストとし、画面上にはMementoのパスをリスト表示したい場合などにはCellFactoryを使用することになるので簡単にメモ。
ちなみに、MWではPublishedMementoListで使用している。
参考としたのは、Ensemble(Java SE Downloads)のList View Cell Factoryと、IT Proさんの記事(Java技術最前線 - JavaFX 2で始めるGUI開発 第5回 リスト、コンボボックス、テーブル:ITpro)。

コード関連箇所の抜粋(PublishedMementoListController)
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;

public class PublishedMementoListController extends AnchorPane implements MWSceneController {
	// 画面項目
	@FXML private Label typeLabel;
	@FXML private ListView<Memento> listView;
	private ObservableList<Memento> listRecords = FXCollections.observableArrayList();

	/**
	 * ListViewにメメント一覧を設定する
	 */
	private void fillMementoListView(String category){
		for(Memento me:processor.findMementoProcess(category)){
			logger.info("メメント["+me+"]を追加します。");
			listRecords.add(me);
		}
		listView.setItems(listRecords);
		listView.setCellFactory(new Callback<ListView<Memento>, ListCell<Memento>>() {
			@Override
			public ListCell<Memento> call(ListView<Memento> arg0) {
				return new MementoCell();
			}
		});
	}

	/**
	 * ListView<Memento>表示用のセル
	 * @author kazyury
	 */
	private static class MementoCell extends ListCell<Memento> {
		@Override
		protected void updateItem(Memento me, boolean empty) {
			super.updateItem(me, empty);
			if(!empty) {
				setText(me.getProductionPath());
			}
		}
	}
}
ListViewとObservableListの定義

内部データの設定については文字列のListViewとほぼ同様。
異なるのは、ListViewとObservableListの型をMementoで定義するのみ。

@FXML private ListView<Memento> listView;
private ObservableList<Memento> listRecords = FXCollections.observableArrayList();
ObservableListへのデータ追加とListViewとのバインド

基本型と全く同じ。

for(Memento me:processor.findMementoProcess(category)){
	logger.info("メメント["+me+"]を追加します。");
	listRecords.add(me);
}
listView.setItems(listRecords);
ListView用のCellFactory

ListViewにMementoを突っ込んでもListView君は何を描画したらよいか判らないだろうから、Memento用のCellを別途定義する必要がある。
まずは、ListViewにどのCellを使うのかを教える。ここはほぼイディオム的なものなのかな?

listView.setCellFactory(new Callback<ListView<Memento>, ListCell<Memento>>() {
	@Override
	public ListCell<Memento> call(ListView<Memento> arg0) {
		return new MementoCell();
	}
});

また、Memento用のCellはListCellを継承して別途定義して、そのCellで描画する内容を制御するようにする。Cell内容の描画はCell#updateItem()で行うようなので、そこをOverrideして、setTextする際にメメントの実行時環境でのパスを表示するように修正する。

/**
 * ListView<Memento>表示用のセル
 * @author kazyury
 */
private static class MementoCell extends ListCell<Memento> {
	@Override
	protected void updateItem(Memento me, boolean empty) {
		super.updateItem(me, empty);
		if(!empty) {
			setText(me.getProductionPath());
		}
	}
}

このような形になる。
f:id:kazyury:20130406234809p:plain