我是JavaFX的新手,我试图在JavaFX中完成这个原则:我已经有一个填充了Student对象的TableView。我希望第一列成为一个复选框,我可以使用该复选框选择每一行以对所选项执行批量操作(如邮件应用程序中常见的那样)。
我认为我不应该将SimpleBooleanProperty添加到Student类,因为它仅用于视图层,这就是为什么我认为我可以像这样实现它:当选中复选框时,学生会被添加列出选定的学生;如果未选中,则将其删除。这是一个好方法吗?
这是我到目前为止所获得的代码(主要基于类似解决方案的复制粘贴):
voornaamKolom.setCellValueFactory(new PropertyValueFactory<Student, String>("name"));
familienaamKolom.setCellValueFactory(new PropertyValueFactory<Student, String>("fname"));
promotorKolom.setCellValueFactory(new PropertyValueFactory<Student, String>("comment"));
selectedKolom.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<Student, Boolean>, ObservableValue<Boolean>>() {
@Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Student, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
selectedKolom.setCellFactory(
new Callback<TableColumn<Student, Boolean>, TableCell<Student, Boolean>>() {
@Override
public TableCell<Student, Boolean> call(TableColumn<Student, Boolean> p) {
return new CheckBoxCell(studentenTabel);
}
});
studentenTabel.getItems().setAll(getModel().getStudenten());
-
private class CheckBoxCell extends TableCell<Student, Boolean> {
final CheckBox cellCheckBox = new CheckBox();
CheckBoxCell(final TableView tblView) {
cellCheckBox.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
int selectedIndex = getTableRow().getIndex();
if (!cellCheckBox.isSelected()) {
getModel().selectStudent(selectedIndex); // add to selectedStudents
} else {
getModel().deselectStudent(selectedIndex); // remove from selectedStudents
}
}
});
}
//Display button if the row is not empty
@Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if (!empty) {
setGraphic(cellCheckBox);
}
}
}
此代码的主要问题是复选框未绑定到表行。例如。当我选择第二个项目并通过对另一个值进行排序来更改行顺序时,即使第二个项目代表另一个对象,它仍然会被选中。当新行添加到表中时,其中一些也会随机选择。
我知道这段代码可能很脏,就像我说的:我是JavaFX的新手。任何帮助将不胜感激。
提前致谢!
答案 0 :(得分:2)
复选框列的数据类型在我看来应该是Student
;即它是TableColumn<Student, Student>
。这样做的原因是您真正呈现了整个对象本身的视图:学生是否包含在所选学生的集合中。有点违反直觉,但它使它有效。
看看这个例子是否有帮助。我没有将数据很好地分离到您的代码提示的模型中,但您也应该能够将其考虑在内。
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SelectableTable extends Application {
@Override
public void start(Stage primaryStage) {
TableView<Item> itemTable = new TableView<>();
for (int i=1; i<=40; i++) {
itemTable.getItems().add(new Item("Item "+i));
}
TableColumn<Item, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<Item, Item> selectedCol = new TableColumn<>("Select");
// Collection of items currently selected via checkboxes in the table
// This will be passed to the TableCell implementation.
ObservableSet<Item> selectedItems = FXCollections.observableSet();
selectedCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item,Item>, ObservableValue<Item>>() {
@Override
public ObservableValue<Item> call(CellDataFeatures<Item, Item> data) {
return new ReadOnlyObjectWrapper<>(data.getValue());
}
});
selectedCol.setCellFactory(new Callback<TableColumn<Item, Item>, TableCell<Item, Item>>() {
@Override
public TableCell<Item, Item> call(
TableColumn<Item, Item> param) {
return new CheckBoxCell(selectedItems);
}
});
itemTable.getColumns().addAll(selectedCol, nameCol);
Button displayButton = new Button("Display selected");
displayButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
for (Item item : selectedItems) {
System.out.println(item.getName());
}
}
});
Button selectAllButton = new Button("Select all");
selectAllButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
selectedItems.addAll(itemTable.getItems());
}
});
Button selectNoneButton = new Button("Select none");
selectNoneButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
selectedItems.clear();
}
});
HBox buttons = new HBox(5);
buttons.getChildren().addAll(selectAllButton, selectNoneButton, displayButton);
BorderPane root = new BorderPane();
root.setCenter(itemTable);
root.setBottom(buttons);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class CheckBoxCell extends TableCell<Item, Item> {
private final ObservableSet<Item> selectedItems ;
private final CheckBox checkBox ;
public CheckBoxCell(ObservableSet<Item> selectedItems) {
this.selectedItems = selectedItems ;
this.checkBox = new CheckBox() ;
// listener to update the set of selected items when the
// check box is checked or unchecked:
checkBox.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (checkBox.isSelected()) {
selectedItems.add(getItem());
} else {
selectedItems.remove(getItem());
}
}
});
// listener to update the check box when the collection of selected
// items changes:
selectedItems.addListener(new SetChangeListener<Item>() {
@Override
public void onChanged(Change<? extends Item> change) {
Item item = getItem();
if (item != null) {
checkBox.setSelected(selectedItems.contains(item));
}
}
});
}
@Override
public void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
checkBox.setSelected(selectedItems.contains(item));
setGraphic(checkBox);
}
}
}
public static class Item {
private final StringProperty name = new SimpleStringProperty(this, "name");
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
public Item(String name) {
setName(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
您可以实施getModel().selectStudent(selectedObject);
而getModel().deselectStudent(selectedObject);
代替。在这里,selectedObject
应该是对象本身而不仅仅是索引。模型应指向对象而不是索引。
最好记住,您可以使模型指向您创建的确切对象。您只需要确保指向正确的对象。