javafx
TableView
수색…
2 열의 샘플 TableView
표 항목
다음의 클래스에는 name ( String
)과 size ( double
)의 2 개의 property가 있습니다. 두 속성은 TableView
가 변경 사항을 관찰 할 수 있도록 JavaFX 속성에 래핑됩니다.
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
를 보여줍니다. 하나는 이름을위한 것이고 다른 하나는 Person
의 크기를위한 것입니다. Person
중 하나를 선택하면 TableView
아래의 TextField
데이터가 추가되고 사용자가 데이터를 편집 할 수 있습니다. 일단 편집이 커밋되면 TableView
가 자동으로 업데이트됩니다.
TableView
추가 된 모든 TableColumn
에 대해 cellValueFactory
가 할당됩니다. 이 팩토리는 테이블 셀에 표시되어야하는 값을 포함하고이 값에 대한 모든 변경 사항을 TableView
가 수신 할 수있게하는 ObservableValue
로 테이블 항목 ( Person
)을 변환합니다.
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>
패턴과 일치해야합니다. 여기서<Constructor parameter>
는 대문자로 시작합니다. 이 메소드는 매개 변수를 가져서는 안됩니다.
메소드의 샘플 이름
생성자 매개 변수 (따옴표 제외) | 속성 메서드의 이름 | getter 메서드의 이름 |
---|---|---|
푸 | fooProperty | getFoo |
fooBar | fooBarProperty | getFooBar |
XYZ | XYZ 속성 | getXYZ |
listIndex | listIndexProperty | getListIndex |
가치 | aValueProperty | getAValue |
항목에 따라 TableCell 모양 사용자 정의
때때로 열은 셀 항목의 toString
값과 다른 내용을 표시해야합니다. 이 경우 TableColumn
의 cellFactory
에 의해 생성 된 TableCell
은 항목을 기반으로 레이아웃을 변경하도록 사용자 정의됩니다.
중요한 정보 : TableView
는 UI에 표시된 TableCell
만 생성합니다. 셀 안의 항목이 변경되어 비어있을 수 있습니다. 프로그래머는 TableCell
이 제거 될 때 항목이 추가 될 때 수행 된 모든 변경 사항을 취소하기 위해주의를 기울여야합니다. 그렇지 않으면 내용이 "속하지 않는"셀에 계속 표시 될 수 있습니다.
아래의 예에서 항목을 설정하면 텍스트가 설정되고 ImageView
표시되는 이미지가 설정됩니다.
image.setImage(item.getEmoji());
setText(item.getValue());
항목이 null
되거나 셀이 비게되면 값을 null
다시 설정하여 변경 사항을 취소합니다.
setText(null);
image.setImage(null);
다음 예제는 TableCell
텍스트 외에도 그림 이모티콘을 보여 TableCell
.
updateItem
메서드는 Cell
의 항목이 변경 될 때마다 호출됩니다. 이 메서드를 재정의하면 변경 내용에 반응하고 셀 모양을 조정할 수 있습니다. Listener를 셀의 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에 단추 추가
setCellFactory(Callback value)
메소드를 사용하여 Tableview에 버튼 또는 다른 javafx 컴포넌트를 추가 할 수 있습니다.
샘플 응용 프로그램
이 애플리케이션에서는 TableView에 버튼을 추가 할 것입니다. 이 열 버튼을 클릭하면 버튼과 같은 행에있는 데이터가 선택되고 해당 정보가 인쇄됩니다.
addButtonToTable()
메서드에서 cellFactory
콜백은 관련 열에 버튼을 추가하는 역할을합니다. 호출 가능한 cellFactory를 정의하고 오버라이드 call(...)
메소드를 구현하여 버튼으로 TableCell
을 cellFactory
다음이 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;
}
}
}