javafx
TableView
サーチ…
2列のサンプルTableView
テーブルアイテム
次のクラスは、name( String
)とsize( double
)の2つのプロパティを含みます。どちらのプロパティもJavaFXプロパティにラップされているため、 TableView
は変更を監視できます。
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
public Person(String name, double size) {
this.size = new SimpleDoubleProperty(this, "size", size);
this.name = new SimpleStringProperty(this, "name", name);
}
private final StringProperty name;
private final DoubleProperty size;
public final String getName() {
return this.name.get();
}
public final void setName(String value) {
this.name.set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final double getSize() {
return this.size.get();
}
public final void setSize(double value) {
this.size.set(value);
}
public final DoubleProperty sizeProperty() {
return this.size;
}
}
サンプルアプリケーション
このアプリケーションは2列のTableView
を示しています。 1つは名前のためのもので、もう1つはPerson
サイズのものです。 Person
の1つを選択すると、 TableView
の下のTextField
データが追加され、ユーザーはデータを編集できます。編集がコミットされると、 TableView
が自動的に更新されます。
TableView
追加されたすべてのTableColumn
TableView
、 cellValueFactory
が割り当てられます。このファクトリは、表の項目( Person
)を表セルに表示する必要がある値を含むObservableValue
に変換し、 TableView
がこの値の変更をリッスンできるようにします。
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class TableSample extends Application {
@Override
public void start(Stage primaryStage) {
// data for the tableview. modifying this list automatically updates the tableview
ObservableList<Person> data = FXCollections.observableArrayList(
new Person("John Doe", 1.75),
new Person("Mary Miller", 1.70),
new Person("Frank Smith", 1.80),
new Person("Charlotte Hoffman", 1.80)
);
TableView<Person> tableView = new TableView<>(data);
// table column for the name of the person
TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> param) {
return param.getValue().nameProperty();
}
});
// column for the size of the person
TableColumn<Person, Number> sizeColumn = new TableColumn<>("Size");
sizeColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Number>, ObservableValue<Number>>() {
@Override
public ObservableValue<Number> call(TableColumn.CellDataFeatures<Person, Number> param) {
return param.getValue().sizeProperty();
}
});
// add columns to tableview
tableView.getColumns().addAll(nameColumn, sizeColumn);
TextField name = new TextField();
TextField size = new TextField();
// convert input from textfield to double
TextFormatter<Double> sizeFormatter = new TextFormatter<Double>(new StringConverter<Double>() {
@Override
public String toString(Double object) {
return object == null ? "" : object.toString();
}
@Override
public Double fromString(String string) {
if (string == null || string.isEmpty()) {
return null;
} else {
try {
double val = Double.parseDouble(string);
return val < 0 ? null : val;
} catch (NumberFormatException ex) {
return null;
}
}
}
});
size.setTextFormatter(sizeFormatter);
Button commit = new Button("Change Item");
commit.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Person p = tableView.getSelectionModel().getSelectedItem();
p.setName(name.getText());
Double value = sizeFormatter.getValue();
p.setSize(value == null ? -1d : value);
}
});
// listen for changes in the selection to update the data in the textfields
tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Person>() {
@Override
public void changed(ObservableValue<? extends Person> observable, Person oldValue, Person newValue) {
commit.setDisable(newValue == null);
if (newValue != null) {
sizeFormatter.setValue(newValue.getSize());
name.setText(newValue.getName());
}
}
});
HBox editors = new HBox(5, new Label("Name:"), name, new Label("Size: "), size, commit);
VBox root = new VBox(10, tableView, editors);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
PropertyValueFactory
PropertyValueFactory
は、 TableColumn
cellValueFactory
として使用できます。特定のパターンと一致するメソッドにアクセスするためにリフレクションを使用して、 TableView
アイテムからデータを取得します。
例
TableColumn<Person, String> nameColumn = ...
PropertyValueFactory<Person, String> valueFactory = new PropertyValueFactory<>("name");
nameColumn.setCellValueFactory(valueFactory);
データを取得するために使用されるメソッドの名前は、 PropertyValueFactory
コンストラクターパラメーターで決まります。
- プロパティメソッド:この種のメソッドは、データを含む
ObservableValue
を返すことが期待されます。変更が見られます。それらは、<constructor parameter>Property
パターンのパターンと一致し、<constructor parameter>Property
を取る必要がありません。 - Getterメソッド:この種のメソッドは、値を直接返します(上記の例では
String
)。メソッド名は、パターンget<Constructor parameter>
と一致する必要がありget<Constructor parameter>
。ここで<Constructor parameter>
は大文字で始まります 。このメソッドはパラメータを取るべきではありません。
メソッドのサンプル名
コンストラクタパラメータ(引用符なし) | プロパティメソッドの名前 | ゲッターメソッドの名前 |
---|---|---|
foo | fooProperty | getFoo |
fooBar | fooBarProperty | getFooBar |
XYZ | XYZプロパティ | getXYZ |
listIndex | listIndexProperty | getListIndex |
価値 | aValueProperty | getAValue |
アイテムに応じてTableCellの外観をカスタマイズする
列には、セル項目のtoString
値とは異なる内容が表示されることがあります。この場合、 TableColumn
のcellFactory
によって作成されたTableCell
は、項目に基づいてレイアウトを変更するようにカスタマイズされています。
重要な注意: TableView
は、UIに表示されるTableCell
のみを作成しTableCell
。セル内のアイテムは変化し、空になることさえあります。プログラマーは、削除されたときにアイテムが追加されたときに行われたTableCell
変更を元に戻すように注意する必要がありTableCell
。それ以外の場合、コンテンツは「それが属していない」セルに表示されることがあります。
以下の例では、項目を設定すると、 ImageView
表示される画像と同様にテキストが設定されます。
image.setImage(item.getEmoji());
setText(item.getValue());
項目がnull
たり、セルが空になったりすると、値をnull
戻すことで変更が取り消されnull
。
setText(null);
image.setImage(null);
次の例は、 TableCell
テキストに加えて、絵文字を示していTableCell
。
updateItem
メソッドは、 Cell
の項目が変更されるたびに呼び出されます。このメソッドをオーバーライドすると、変更に反応してセルの外観を調整できます。リスナーをセルのitemProperty()
に追加する方法もありTableCell
が、多くの場合、 TableCell
が拡張されています。
アイテムタイプ
import javafx.scene.image.Image;
// enum providing image and text for certain feelings
public enum Feeling {
HAPPY("happy", "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Emojione_1F600.svg/64px-Emojione_1F600.svg.png"),
SAD("sad", "https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Emojione_1F62D.svg/64px-Emojione_1F62D.svg.png")
;
private final Image emoji;
private final String value;
Feeling(String value, String url) {
// load image in background
emoji = new Image(url, true);
this.value = value;
}
public Image getEmoji() {
return emoji;
}
public String getValue() {
return value;
}
}
アプリケーションクラスのコード
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class EmotionTable extends Application {
public static class Item {
private final ObjectProperty<Feeling> feeling;
public Item(Feeling feeling) {
this.feeling = new SimpleObjectProperty<>(feeling);
}
public final Feeling getFeeling() {
return this.feeling.get();
}
public final void setFeeling(Feeling value) {
this.feeling.set(value);
}
public final ObjectProperty<Feeling> feelingProperty() {
return this.feeling;
}
}
@Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>(FXCollections.observableArrayList(
new Item(Feeling.HAPPY),
new Item(Feeling.HAPPY),
new Item(Feeling.HAPPY),
new Item(Feeling.SAD),
null,
new Item(Feeling.HAPPY),
new Item(Feeling.HAPPY),
new Item(Feeling.SAD)
));
EventHandler<ActionEvent> eventHandler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// change table items depending on userdata of source
Node source = (Node) event.getSource();
Feeling targetFeeling = (Feeling) source.getUserData();
for (Item item : table.getItems()) {
if (item != null) {
item.setFeeling(targetFeeling);
}
}
}
};
TableColumn<Item, Feeling> feelingColumn = new TableColumn<>("Feeling");
feelingColumn.setCellValueFactory(new PropertyValueFactory<>("feeling"));
// use custom tablecell to display emoji image
feelingColumn.setCellFactory(new Callback<TableColumn<Item, Feeling>, TableCell<Item, Feeling>>() {
@Override
public TableCell<Item, Feeling> call(TableColumn<Item, Feeling> param) {
return new EmojiCell<>();
}
});
table.getColumns().add(feelingColumn);
Button sunshine = new Button("sunshine");
Button rain = new Button("rain");
sunshine.setOnAction(eventHandler);
rain.setOnAction(eventHandler);
sunshine.setUserData(Feeling.HAPPY);
rain.setUserData(Feeling.SAD);
Scene scene = new Scene(new VBox(10, table, new HBox(10, sunshine, rain)));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
セルクラス
import javafx.scene.control.TableCell;
import javafx.scene.image.ImageView;
public class EmojiCell<T> extends TableCell<T, Feeling> {
private final ImageView image;
public EmojiCell() {
// add ImageView as graphic to display it in addition
// to the text in the cell
image = new ImageView();
image.setFitWidth(64);
image.setFitHeight(64);
image.setPreserveRatio(true);
setGraphic(image);
setMinHeight(70);
}
@Override
protected void updateItem(Feeling item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
// set back to look of empty cell
setText(null);
image.setImage(null);
} else {
// set image and text for non-empty cell
image.setImage(item.getEmoji());
setText(item.getValue());
}
}
}
Tableviewにボタンを追加する
setCellFactory(Callback value)
メソッドの列を使用して、Tableviewにボタンまたは別のjavafxコンポーネントを追加できます。
サンプルアプリケーション
このアプリケーションでは、TableViewにボタンを追加します。この列ボタンをクリックすると、ボタンと同じ行のデータが選択され、その情報が印刷されます。
addButtonToTable()
メソッドでは、 cellFactory
コールバックが関連する列にボタンを追加します。呼び出し可能なcellFactoryを定義し、オーバーライド・call(...)
メソッドを実装して、ボタンでTableCell
を取得し、次にこのcellFactory
を関連列setCellFactory(..)
メソッドに設定します。サンプルでは、これはcolBtn.setCellFactory(cellFactory)
です。 SSCCEは以下の通りです:
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewSample extends Application {
private final TableView<Data> table = new TableView<>();
private final ObservableList<Data> tvObservableList = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
stage.setTitle("Tableview with button column");
stage.setWidth(600);
stage.setHeight(600);
setTableappearance();
fillTableObservableListWithSampleData();
table.setItems(tvObservableList);
TableColumn<Data, Integer> colId = new TableColumn<>("ID");
colId.setCellValueFactory(new PropertyValueFactory<>("id"));
TableColumn<Data, String> colName = new TableColumn<>("Name");
colName.setCellValueFactory(new PropertyValueFactory<>("name"));
table.getColumns().addAll(colId, colName);
addButtonToTable();
Scene scene = new Scene(new Group(table));
stage.setScene(scene);
stage.show();
}
private void setTableappearance() {
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setPrefWidth(600);
table.setPrefHeight(600);
}
private void fillTableObservableListWithSampleData() {
tvObservableList.addAll(new Data(1, "app1"),
new Data(2, "app2"),
new Data(3, "app3"),
new Data(4, "app4"),
new Data(5, "app5"));
}
private void addButtonToTable() {
TableColumn<Data, Void> colBtn = new TableColumn("Button Column");
Callback<TableColumn<Data, Void>, TableCell<Data, Void>> cellFactory = new Callback<TableColumn<Data, Void>, TableCell<Data, Void>>() {
@Override
public TableCell<Data, Void> call(final TableColumn<Data, Void> param) {
final TableCell<Data, Void> cell = new TableCell<Data, Void>() {
private final Button btn = new Button("Action");
{
btn.setOnAction((ActionEvent event) -> {
Data data = getTableView().getItems().get(getIndex());
System.out.println("selectedData: " + data);
});
}
@Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(btn);
}
}
};
return cell;
}
};
colBtn.setCellFactory(cellFactory);
table.getColumns().add(colBtn);
}
public class Data {
private int id;
private String name;
private Data(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int ID) {
this.id = ID;
}
public String getName() {
return name;
}
public void setName(String nme) {
this.name = nme;
}
@Override
public String toString() {
return "id: " + id + " - " + "name: " + name;
}
}
}