javafx
TableView
Поиск…
Пример TableView с двумя столбцами
Таблица
Следующий класс содержит 2 свойства: имя ( String
) и размер ( double
). Оба свойства завернуты в свойства 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;
}
}
Пример приложения
Это приложение отображает TableView
с двумя столбцами; один для имени и один для размера Person
. Выбор одного из Person
s добавляет данные в TextField
s ниже TableView
и позволяет пользователю редактировать данные. Обратите внимание, как только редактирование завершено, TableView
автоматически обновляется.
Каждому для каждого TableColumn
добавленному в TableView
, присваивается cellValueFactory
. Эта фабрика отвечает за преобразование элементов таблицы ( Person
's) в ObservableValue
s, которые содержат значение, которое должно отображаться в ячейке таблицы, и которое позволяет 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
может использоваться как cellValueFactory
в TableColumn
. Он использует отражение для доступа к методам, которые соответствуют определенному шаблону для извлечения данных из элемента TableView
:
пример
TableColumn<Person, String> nameColumn = ...
PropertyValueFactory<Person, String> valueFactory = new PropertyValueFactory<>("name");
nameColumn.setCellValueFactory(valueFactory);
Имя метода, используемого для получения данных, зависит от параметра-параметра конструктора для PropertyValueFactory
.
- Метод свойства: ожидается, что этот вид метода возвращает
ObservableValue
содержащий данные. Могут наблюдаться изменения. Они должны соответствовать шаблону<constructor parameter>Property
и не принимать никаких параметров. - Метод Getter: этот метод предполагает возврат значения напрямую (
String
в приведенном выше примере). Имя метода должно соответствовать шаблонуget<Constructor parameter>
. Обратите внимание, что здесь<Constructor parameter>
начинается с прописной буквы . Этот метод не должен принимать параметры.
Примеры имен методов
параметр конструктора (без кавычек) | название метода собственности | имя метода геттера |
---|---|---|
Foo | fooProperty | getFoo |
Foobar | fooBarProperty | getFooBar |
XYZ | XYZProperty | getXYZ |
ListIndex | listIndexProperty | getListIndex |
ценность | aValueProperty | getAValue |
Настройка TableCell выглядит в зависимости от элемента
Иногда в столбце должен отображаться другой контент, чем только значение toString
элемента ячейки. В этом случае TableCell
s, созданный cellFactory
TableColumn
, настроен для изменения макета на основе элемента.
Важное примечание. TableView
создает только TableCell
s, которые показаны в пользовательском интерфейсе. Элементы внутри ячеек могут меняться и даже становиться пустыми. Программисту необходимо позаботиться о том, чтобы отменить любые изменения в TableCell
которые были сделаны, когда элемент был добавлен при его удалении. В противном случае содержимое может отображаться в ячейке, где «она не принадлежит».
В приведенном ниже примере установка элемента приводит к тому, что текст будет установлен, а также изображение, отображаемое в ImageView
:
image.setImage(item.getEmoji());
setText(item.getValue());
Если элемент становится null
или ячейка становится пустой, эти изменения отменены, если значения вернутся к null
значению:
setText(null);
image.setImage(null);
Следующий пример показывает emoji в дополнение к тексту в TableCell
.
Метод updateItem
вызывается каждый раз, когда элемент Cell
изменяется. Переопределение этого метода позволяет реагировать на изменения и корректировать внешний вид ячейки. Добавление слушателя к itemProperty()
ячейки было бы альтернативой, но во многих случаях 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
Вы можете добавить кнопку или другой компонент javafx в Tableview, используя setCellFactory(Callback value)
.
Пример приложения
В этом приложении мы добавим кнопку в TableView. При нажатии на эту кнопку столбца выбираются данные в той же строке, что и кнопка, и распечатывается ее информация.
В addButtonToTable()
cellFactory
вызов cellFactory
отвечает за добавление кнопки в соответствующий столбец. Мы определяем вызываемый cellFactory и реализуем его метод override 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;
}
}
}