Javaデスクトップアプリケーションでは、CheckBoxを含む列を作成したいTableViewがあります。
これが行われた場所を見つけました http://www.jonathangiles.net/javafx/2.0/CellFactories/ 私が尋ねると思った私のメールに答えてください...
TableViewのセルにCheckBoxを配置するにはどうすればよいですか?
TableColumnにCellFactoryを設定する必要があります。
例えば:
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 );
UPDATE: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 );
行数は2で割られます! (16 ==> 8)
UPDATE:Java 10 "var" contextual Word =を使用した同じコード
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 );
編集して完全な機能の編集可能な例を追加(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>
<?import com.assylias.factories.*?>
などのインポートをFXMLに追加する必要もあります
ボーナス:たとえば、次のようなフィールドを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-isUnemployedメソッドを持つ「Person」オブジェクトがあるとします:
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);
}
}
-コールバックをテーブル列にバインドします
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-それだけです
チェックボックスをクリックするとバッキングBeanに値が設定され、テーブルにアイテムリストをロードするとチェックボックス値が正しく設定され、すべてが期待どおりに動作するはずです。
これは、モデルをビューと同期させる方法を示す完全な作業例です。
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();
}
}
編集可能なチェックボックスをモデルにリンクするための最も簡単な解決策は次のとおりです。
「name」文字列と「selected」ブール値の2つのフィールドを持つ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")
);
}
これが私のために働いたものです(私のモデルのオブジェクトはCandidate
であり、チェックボックスはそれらが除外されるかどうかを決定することに注意してください、したがってisExcluded()
):
tableColumnCandidateExcluded.setCellValueFactory(
c -> {
Candidate candidate = c.getValue();
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().setValue(candidate.isExcluded());
checkBox
.selectedProperty()
.addListener((ov, old_val, new_val) -> candidate.setExcluded(new_val));
return new SimpleObjectProperty(checkBox);
});
これがやり方です
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);
}
};
});
cb.setFocusTraversable(false)は、フォーカスがスタックしないようにするために必要です。
setGraphic(null)は、アイテムを削除した後、またはソースリストが変更されたときに残されたものをすべて消去するために必要です。
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を使用した場合と同じです
以前の回答からヒントを得た、これは可能な限り短いバージョンだと思います。
checkBoxColumn.setCellValueFactory(c -> {
c.getValue().booleanProperty().addListener((ch, o, n) -> {
// do something
});
return c.getValue().booleanProperty();
});
checkBoxColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkBoxColumn));