Как добавить флажок в TableView в JavaFX
в моем настольном приложении Java у меня есть TableView, в котором я хочу иметь столбец с флажками.
Я нашел, где это было сделано http://www.jonathangiles.net/javafx/2.0/CellFactories/ но поскольку загрузка недоступна, и потому что я не знаю, как скоро Джонатан Джайлс ответит на мою электронную почту, я подумал, что спрошу...
Как установить флажок в ячейке моего TableView?
12 ответов
вам нужно установить CellFactory на TableColumn.
например:
Callback<TableColumn<TableData, Boolean>, TableCell<TableData, Boolean>> booleanCellFactory =
new Callback<TableColumn<TableData, Boolean>, TableCell<TableData, Boolean>>() {
@Override
public TableCell<TableData, Boolean> call(TableColumn<TableData, Boolean> p) {
return new BooleanCell();
}
};
active.setCellValueFactory(new PropertyValueFactory<TableData,Boolean>("active"));
active.setCellFactory(booleanCellFactory);
class BooleanCell extends TableCell<TableData, Boolean> {
private CheckBox checkBox;
public BooleanCell() {
checkBox = new CheckBox();
checkBox.setDisable(true);
checkBox.selectedProperty().addListener(new ChangeListener<Boolean> () {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(isEditing())
commitEdit(newValue == null ? false : newValue);
}
});
this.setGraphic(checkBox);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.setEditable(true);
}
@Override
public void startEdit() {
super.startEdit();
if (isEmpty()) {
return;
}
checkBox.setDisable(false);
checkBox.requestFocus();
}
@Override
public void cancelEdit() {
super.cancelEdit();
checkBox.setDisable(true);
}
public void commitEdit(Boolean value) {
super.commitEdit(value);
checkBox.setDisable(true);
}
@Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
checkBox.setSelected(item);
}
}
}
использует javafx.scene.control.cell.CheckBoxTableCell<S,T>
и работа сделана !
ObservableList< TableColumn< RSSReader, ? >> columns =
_rssStreamsView.getColumns();
[...]
TableColumn< RSSReader, Boolean > loadedColumn = new TableColumn<>( "Loaded" );
loadedColumn.setCellValueFactory(
new Callback<CellDataFeatures<RSSReader,Boolean>,ObservableValue<Boolean>>(){
@Override public
ObservableValue<Boolean> call( CellDataFeatures<RSSReader,Boolean> p ){
return p.getValue().getCompleted(); }});
loadedColumn.setCellFactory(
new Callback<TableColumn<RSSReader,Boolean>,TableCell<RSSReader,Boolean>>(){
@Override public
TableCell<RSSReader,Boolean> call( TableColumn<RSSReader,Boolean> p ){
return new CheckBoxTableCell<>(); }});
[...]
columns.add( loadedColumn );
обновление: тот же код, используя Java 8 лямбда-выражения
ObservableList< TableColumn< RSSReader, ? >> columns =
_rssStreamsView.getColumns();
[...]
TableColumn< RSSReader, Boolean > loadedColumn = new TableColumn<>( "Loaded" );
loadedColumn.setCellValueFactory( f -> f.getValue().getCompleted());
loadedColumn.setCellFactory( tc -> new CheckBoxTableCell<>());
[...]
columns.add( loadedColumn );
количество строк делится на два! (16 ==> 8)
обновление: тот же код, используя Java 10 "var" контекстное слово
var columns = _rssStreamsView.getColumns();
[...]
var loadedColumn = new TableColumn<RSSReader, Boolean>( "Loaded" );
loadedColumn.setCellValueFactory( f -> f.getValue().getCompleted());
loadedColumn.setCellFactory( tc -> new CheckBoxTableCell<>());
[...]
columns.add( loadedColumn );
EDIT для добавления полнофункционального редактируемого примера (Java 8)
public class Os {
private final StringProperty name = new SimpleStringProperty();
private final BooleanProperty delete = new SimpleBooleanProperty();
public Os( String nm, boolean del ) {
name .set( nm );
delete.set( del );
}
public StringProperty nameProperty () { return name; }
public BooleanProperty deleteProperty() { return delete; }
}
public class FxEditableCheckBox extends Application {
@Override
public void start( Stage stage ) throws Exception {
final TableView<Os> view = new TableView<>();
final ObservableList<TableColumn<Os, ?>> columns = view.getColumns();
final TableColumn<Os, Boolean> nameColumn = new TableColumn<>( "Name" );
nameColumn.setCellValueFactory( new PropertyValueFactory<>( "name" ));
columns.add( nameColumn );
final TableColumn<Os, Boolean> loadedColumn = new TableColumn<>( "Delete" );
loadedColumn.setCellValueFactory( new PropertyValueFactory<>( "delete" ));
loadedColumn.setCellFactory( tc -> new CheckBoxTableCell<>());
columns.add( loadedColumn );
final ObservableList<Os> items =
FXCollections.observableArrayList(
new Os( "Microsoft Windows 3.1" , true ),
new Os( "Microsoft Windows 3.11" , true ),
new Os( "Microsoft Windows 95" , true ),
new Os( "Microsoft Windows NT 3.51", true ),
new Os( "Microsoft Windows NT 4" , true ),
new Os( "Microsoft Windows 2000" , true ),
new Os( "Microsoft Windows Vista" , true ),
new Os( "Microsoft Windows Seven" , false ),
new Os( "Linux all versions :-)" , false ));
view.setItems( items );
view.setEditable( true );
final Button delBtn = new Button( "Delete" );
delBtn.setMaxWidth( Double.MAX_VALUE );
delBtn.setOnAction( e -> {
final Set<Os> del = new HashSet<>();
for( final Os os : view.getItems()) {
if( os.deleteProperty().get()) {
del.add( os );
}
}
view.getItems().removeAll( del );
});
stage.setScene( new Scene( new BorderPane( view, null, null, delBtn, null )));
BorderPane.setAlignment( delBtn, Pos.CENTER );
stage.show();
}
public static void main( String[] args ) {
launch( args );
}
}
изменить для добавления полнофункциональный редактируемый пример (Java 10)
public class Os {
private final StringProperty name = new SimpleStringProperty();
private final BooleanProperty delete = new SimpleBooleanProperty();
public Os( String nm, boolean del ) {
name .set( nm );
delete.set( del );
}
public StringProperty nameProperty () { return name; }
public BooleanProperty deleteProperty() { return delete; }
}
public class FxEditableCheckBoxJava10 extends Application {
@Override
public void start( Stage stage ) throws Exception {
final var view = new TableView<Os>();
final var columns = view.getColumns();
final var nameColumn = new TableColumn<Os, Boolean>( "Name" );
nameColumn.setCellValueFactory( new PropertyValueFactory<>( "name" ));
columns.add( nameColumn );
final var loadedColumn = new TableColumn<Os, Boolean>( "Delete" );
loadedColumn.setCellValueFactory( new PropertyValueFactory<>( "delete" ));
loadedColumn.setCellFactory( tc -> new CheckBoxTableCell<>());
columns.add( loadedColumn );
final var items = FXCollections.observableArrayList(
new Os( "Microsoft Windows 3.1" , true ),
new Os( "Microsoft Windows 3.11" , true ),
new Os( "Microsoft Windows 95" , true ),
new Os( "Microsoft Windows NT 3.51", true ),
new Os( "Microsoft Windows NT 4" , true ),
new Os( "Microsoft Windows 2000" , true ),
new Os( "Microsoft Windows Vista" , true ),
new Os( "Microsoft Windows Seven" , false ),
new Os( "Linux all versions :-)" , false ));
view.setItems( items );
view.setEditable( true );
final var delBtn = new Button( "Delete" );
delBtn.setMaxWidth( Double.MAX_VALUE );
delBtn.setOnAction( e -> {
final var del = new HashSet<Os>();
for( final var os : view.getItems()) {
if( os.deleteProperty().get()) {
del.add( os );
}
}
view.getItems().removeAll( del );
});
stage.setScene( new Scene( new BorderPane( view, null, null, delBtn, null )));
BorderPane.setAlignment( delBtn, Pos.CENTER );
stage.show();
}
public static void main( String[] args ) {
launch( args );
}
}
TableColumn select = new TableColumn("CheckBox");
select.setMinWidth(200);
select.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, CheckBox>, ObservableValue<CheckBox>>() {
@Override
public ObservableValue<CheckBox> call(
TableColumn.CellDataFeatures<Person, CheckBox> arg0) {
Person user = arg0.getValue();
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().setValue(user.isSelected());
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
user.setSelected(new_val);
}
});
return new SimpleObjectProperty<CheckBox>(checkBox);
}
});
table.getColumns().addAll( select);
самое простое решение, вероятно, сделать это в FXML:
-
сначала создайте класс ниже:
public class CheckBoxCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> { @Override public TableCell<S, T> call(TableColumn<S, T> p) { return new CheckBoxTableCell<>(); } }
-
затем включите фабрику ячеек в свой FXML:
<TableColumn text="Select" fx:id="selectColumn" > <cellFactory> <CheckBoxCellFactory/> </cellFactory> </TableColumn>
вам также необходимо добавить импорт в FXML, например <?import com.assylias.factories.*?>
бонус: вы можете сделать фабрику более настраиваемой, например, чтобы определить, где должен появиться флажок, добавив поля в CheckBoxCellFactory
класс, например:
private Pos alignment = Pos.CENTER;
public Pos getAlignment() { return alignment; }
public void setAlignment(Pos alignment) { this.alignment = alignment; }
и FXML:
<cellFactory>
<CheckBoxCellFactory alignment="BOTTOM_RIGHT"/>
</cellFactory>
маленький и простой.
row.setCellValueFactory(c -> new SimpleBooleanProperty(c.getValue().getIsDefault()));
row.setCellFactory(tc -> new CheckBoxTableCell<>());
существует очень простой способ сделать это, вам не нужно изменять свой класс модели с помощью SimpleBooleanProperty или что-то еще, просто выполните следующие действия:
1 - Предположим, у вас есть объект "Person" с isunemployed методом:
public class Person {
private String name;
private Boolean unemployed;
public String getName(){return this.name;}
public void setName(String name){this.name = name;}
public Boolean isUnemployed(){return this.unemployed;}
public void setUnemployed(Boolean unemployed){this.unemployed = unemployed;}
}
2 - Создайте класс обратного вызова
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
public class PersonUnemployedValueFactory implements Callback<TableColumn.CellDataFeatures<Person, CheckBox>, ObservableValue<CheckBox>> {
@Override
public ObservableValue<CheckBox> call(TableColumn.CellDataFeatures<Person, CheckBox> param) {
Person person = param.getValue();
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().setValue(person.isUnemployed());
checkBox.selectedProperty().addListener((ov, old_val, new_val) -> {
person.setUnemployed(new_val);
});
return new SimpleObjectProperty<>(checkBox);
}
}
3-привязать обратный вызов к столбцу таблицы
если вы используете FXML, поместите класс обратного вызова внутрь ваша колонка:
<TableView fx:id="personList" prefHeight="200.0" prefWidth="200.0">
<columns>
<TableColumn prefWidth="196.0" text="Unemployed">
<cellValueFactory>
<PersonUnemployedValueFactory/> <!--This is how the magic happens-->
</cellValueFactory>
</TableColumn>
...
</columns>
</TableView>
Не забудьте импортировать класс в свой FXML:
<?import org.yourcompany.yourapp.util.PersonUnemployedValueFactory?>
без FXML, сделайте это так:
TableColumn<Person, CheckBox> column = (TableColumn<Person, CheckBox>) personTable.getColumns().get(0);
column.setCellValueFactory(new PersonUnemployedValueFactory());
4 - Вот это
все должно работать так, как ожидалось, при этом значение устанавливается в резервный компонент при нажатии на флажок, а значение флажка правильно устанавливается при загрузке списка элементов в таблице.
самое простое решение для того, чтобы иметь редактируемый флажок, связанный с моделью:
при условии, что у вас есть Person
класс модели с двумя полями, строкой" имя "и" выбранным " логическим значением:
public class Person {
private final SimpleBooleanProperty selected;
private final SimpleStringProperty name;
public Person(String name) {
this.selected = new SimpleBooleanProperty(false);
this.name = new SimpleStringProperty(name);
}
public boolean isSelected() {
return selected.get();
}
public SimpleBooleanProperty selectedProperty() {
return selected;
}
public void setSelected(boolean selected) {
this.selected.set(selected);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
}
все, что вам нужно делать в контроллере:
@FXML private TableColumn<Person, Boolean> checkBoxCol;
@FXML private TableColumn<Person, String> nameCol;
@Override
public void initialize(URL location, ResourceBundle resources) {
checkBoxCol.setCellFactory(
CheckBoxTableCell.forTableColumn(checkBoxCol)
);
checkBoxCol.setCellValueFactory(
new PropertyValueFactory<>("selected")
);
nameCol.setCellValueFactory(
new PropertyValueFactory<>("name")
);
}
вот полный рабочий пример, показывающий, как сохранить модель в синхронизации с Вид.....
package org.pauquette.example;
import javafx.application.Application;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
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.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class CheckBoxExample extends Application {
class BooleanCell extends TableCell<TableData, Boolean> {
private CheckBox checkBox;
public BooleanCell() {
checkBox = new CheckBox();
checkBox.setDisable(true);
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (isEditing())
commitEdit(newValue == null ? false : newValue);
}
});
this.setGraphic(checkBox);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.setEditable(true);
}
@Override
public void cancelEdit() {
super.cancelEdit();
checkBox.setDisable(true);
}
public void commitEdit(Boolean value) {
super.commitEdit(value);
checkBox.setDisable(true);
}
@Override
public void startEdit() {
super.startEdit();
if (isEmpty()) {
return;
}
checkBox.setDisable(false);
checkBox.requestFocus();
}
@Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
checkBox.setSelected(item);
}
}
}
// Pojo class. A Javabean
public class TableData {
SimpleBooleanProperty favorite;
SimpleStringProperty stooge;
// A javabean typically has a zero arg constructor
// https://docs.oracle.com/javase/tutorial/javabeans/
public TableData() {
}
// but can have others also
public TableData(String stoogeIn, Boolean favoriteIn) {
stooge = new SimpleStringProperty(stoogeIn);
favorite = new SimpleBooleanProperty(favoriteIn);
}
/**
* @return the stooge
*/
public String getStooge() {
return stooge.get();
}
/**
* @return the favorite
*/
public Boolean isFavorite() {
return favorite.get();
}
/**
* @param favorite
* the favorite to set
*/
public void setFavorite(Boolean favorite) {
this.favorite.setValue(favorite);
}
/**
* @param stooge
* the stooge to set
*/
public void setStooge(String stooge) {
this.stooge.setValue(stooge);
}
}
// Model class - The model in mvc
// Typically a representation of a database or nosql source
public class TableModel {
ObservableList<TableData> stooges = FXCollections.observableArrayList();
public TableModel() {
stooges.add(new TableData("Larry", false));
stooges.add(new TableData("Moe", true));
stooges.add(new TableData("Curly", false));
}
public String displayModel() {
StringBuilder sb=new StringBuilder();
for (TableData stooge : stooges) {
sb.append(stooge.getStooge() + "=" + stooge.isFavorite() + "|");
}
return sb.toString();
}
/**
* @return the stooges
*/
public ObservableList<TableData> getStooges() {
return stooges;
}
public void updateStooge(TableData dataIn) {
int index=stooges.indexOf(dataIn);
stooges.set(index, dataIn);
}
}
public static void main(String[] args) {
launch(args);
}
private TableModel model;
private TableModel getModel() {
if (model == null) {
model = new TableModel();
}
return model;
}
@Override
public void start(Stage primaryStage) throws Exception {
final VBox view=new VBox(10);
final TableView<TableData> table = new TableView<>();
final ObservableList<TableColumn<TableData, ?>> columns = table.getColumns();
final TableModel model = getModel();
final TableColumn<TableData, String> stoogeColumn = new TableColumn<>("Stooge");
stoogeColumn.setCellValueFactory(new PropertyValueFactory<>("stooge"));
columns.add(stoogeColumn);
final Button showModelButton = new Button("Show me the Model, woo,woo,woo");
final Label showModelLabel = new Label("Model? Whats that?");
showModelButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
showModelLabel.setText(model.displayModel());
}});
final TableColumn<TableData, CheckBox> favoriteColumn = new TableColumn<TableData, CheckBox>("Favorite");
favoriteColumn.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<TableData, CheckBox>, ObservableValue<CheckBox>>() {
@Override
public ObservableValue<CheckBox> call(TableColumn.CellDataFeatures<TableData, CheckBox> arg0) {
TableData data = arg0.getValue();
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().setValue(data.isFavorite());
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov, Boolean old_val,
Boolean new_val) {
data.setFavorite(new_val);
checkBox.setSelected(new_val);
model.updateStooge(data);
}
});
return new SimpleObjectProperty<CheckBox>(checkBox);
}
});
columns.add(favoriteColumn);
table.setItems(model.getStooges());
HBox hbox = new HBox(10);
hbox.getChildren().addAll(showModelButton,showModelLabel);
view.getChildren().add(hbox);
view.getChildren().add(table);
Scene scene = new Scene(view, 640, 380);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Это способ сделать это
tbcSingleton.setCellValueFactory(data -> data.getValue().singletonProperty());
tbcSingleton.setCellFactory( param -> {
return new TableCell<FXMLController, Boolean>(){
{
setAlignment(Pos.CENTER);
}
protected void updateItem(Boolean item, boolean empty){
if(!empty && item!=null) {
CheckBox cb = new CheckBox();
cb.setSelected(item);
cb.setFocusTraversable(false);
cb.selectedProperty().addListener((obs,old,niu)->listaFXMLController.get(getIndex()).setSingleton(niu));
setGraphic(cb);
}else
setGraphic(null);
}
};
});
ХБ.setFocusTraversable (false) необходим для предотвращения получения фокуса застрял на нем.
setGraphic (null) необходимо стереть все, что осталось после удаления элемента или при изменении исходного списка
- cb.selectedProperty ().addListener ((obs, old, niu) - >(ваши вещи...)); здесь вы ловите новое значение флажка и делаете все, что хотите он.
вот еще один с ToggleGroup и ToggleButtons
tbcTipoControlador.setCellValueFactory(data -> data.getValue().controllerTypeProperty());
tbcTipoControlador.setCellFactory( param -> {
return new TableCell<FXMLController, ControllerType>() {
{
setAlignment(Pos.CENTER);
}
protected void updateItem(ControllerType item, boolean empty){
if(!empty && item!=null) {
ToggleButton tbModal = new ToggleButton("Modal");
tbModal.selectedProperty().addListener((obs,old,niu)->{
if(niu)
listaFXMLController.get(getIndex()).setControllerType(ControllerType.MODAL);
});
tbModal.setSelected(item.equals(ControllerType.MODAL));
ToggleButton tbPlain = new ToggleButton("Plain");
tbPlain.selectedProperty().addListener((obs,old,niu)->{
if(niu)
listaFXMLController.get(getIndex()).setControllerType(ControllerType.PLAIN);
});
tbPlain.setSelected(item.equals(ControllerType.PLAIN));
ToggleButton tbApplication= new ToggleButton("Application");
tbApplication.selectedProperty().addListener((obs,old,niu)->{
if(niu)
listaFXMLController.get(getIndex()).setControllerType(ControllerType.APPLICATION);
});
tbApplication.setSelected(item.equals(ControllerType.APPLICATION));
ToggleGroup gp = new ToggleGroup();
tbModal.setFocusTraversable(false);
tbPlain.setFocusTraversable(false);
tbApplication.setFocusTraversable(false);
tbModal.setPrefWidth(120);
tbPlain.setPrefWidth(120);
tbApplication.setPrefWidth(120);
gp.getToggles().addAll(tbModal,tbPlain,tbApplication);
HBox hb = new HBox();
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(tbModal,tbPlain,tbApplication);
setGraphic(hb);
}else
setGraphic(null);
}
};
});
Я сделал некоторый тест, и потребление памяти в основном такое же, как с помощью ComboBoxTableCell
Так выглядит мое маленькое приложение (sry, мой основной язык-испанский, и я создаю его для личного использования)
вдохновленный предыдущими ответами, это самая короткая возможная версия, я думаю.
checkBoxColumn.setCellValueFactory(c -> {
c.getValue().booleanProperty().addListener((ch, o, n) -> {
// do something
});
return c.getValue().booleanProperty();
});
checkBoxColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkBoxColumn));
вот 3rd партия lib, которая сделает это http://www.jonathangiles.net/javafx/2.0/CellFactories/docs/api/net/jonathangiles/javafx/control/cell/tableview/CheckBoxTableCell.html
для меня, работает с это решение:
Callback<TableColumn, TableCell> checkboxCellFactory = new Callback<TableColumn, TableCell>() {
@Override
public TableCell call(TableColumn p) {
return new CheckboxCell();
}
};
TableColumn selectColumn = (TableColumn) tbvDatos.getColumns().get(1);
selectColumn.setCellValueFactory(new PropertyValueFactory("selected"));
selectColumn.setCellFactory(checkboxCellFactory);
и tableCell:
public class CheckboxCell extends TableCell<RowData, Boolean> {
CheckBox checkbox;
@Override
protected void updateItem(Boolean arg0, boolean arg1) {
super.updateItem(arg0, arg1);
paintCell();
}
private void paintCell() {
if (checkbox == null) {
checkbox = new CheckBox();
checkbox.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
setItem(new_val);
((RowData)getTableView().getItems().get(getTableRow().getIndex())).setSelected(new_val);
}
});
}
checkbox.setSelected(getValue());
setText(null);
setGraphic(checkbox);
}
private Boolean getValue() {
return getItem() == null ? false : getItem();
}
}
Если вам не нужно устанавливать флажок с edit event