How to add CheckBox's to a TableView in JavaFX

前端 未结 13 503
日久生厌
日久生厌 2020-11-30 00:32

In my Java Desktop Application I have a TableView in which I want to have a column with CheckBoxes.

I did find where this has been done http://www.jonathangiles.net/

相关标签:
13条回答
  • 2020-11-30 01:04

    Uses javafx.scene.control.cell.CheckBoxTableCell<S,T> and the work's done !

      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: same code using Java 8 lambda expressions

      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 );
    

    Lines count is divided by two! (16 ==> 8)

    UPDATE: same code using 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 );
    

    EDIT to add full functional editable example (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 );
       }
    }
    

    EDIT to add full functional editable example (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 );
       }
    }
    
    0 讨论(0)
  • 2020-11-30 01:04

    Here is a complete working example showing how to keep the model in sync with the view.....

    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();
    
        }
    
    }
    
    0 讨论(0)
  • 2020-11-30 01:07

    There is a very simple way of doing this, you don't need to modify your model class with SimpleBooleanProperty or whatever, just follow these steps:

    1 - Suppose you have a "Person" object with a isUnemployed method:

    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 - Create the callback class

    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 - Bind the callback to the table column

    If you use FXML, put the callback class inside your column:

    <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>
    

    Don't forget to import the class in your FXML:

    <?import org.yourcompany.yourapp.util.PersonUnemployedValueFactory?>
    

    Without FXML, do it like this:

    TableColumn<Person, CheckBox> column = (TableColumn<Person, CheckBox>) personTable.getColumns().get(0);
    column.setCellValueFactory(new PersonUnemployedValueFactory());
    

    4 - That's it

    Everything should work as expected, with the value being set to the backing bean when you click on the checkbox, and the checkbox value correctly being set when you load the items list in your table.

    0 讨论(0)
  • 2020-11-30 01:11

    The simplest solution is probably to do it in FXML:

    1. First create the class below:

      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<>();
          }
      }
      
    2. Then include a cell factory in your FXML:

      <TableColumn text="Select" fx:id="selectColumn" >
          <cellFactory>
              <CheckBoxCellFactory/>
          </cellFactory>
      </TableColumn>
      

    You also need to add an import in the FXML, such as <?import com.assylias.factories.*?>


    Bonus: you can make the factory more customisable, for example to determine where the checkbox should appear, by adding fields to the CheckBoxCellFactory class, such as:

    private Pos alignment = Pos.CENTER;
    public Pos getAlignment() { return alignment; }
    public void setAlignment(Pos alignment) { this.alignment = alignment; }
    

    And the FXML:

    <cellFactory>
        <CheckBoxCellFactory alignment="BOTTOM_RIGHT"/>
    </cellFactory>
    
    0 讨论(0)
  • 2020-11-30 01:11

    Inspired from the previous answers, this is the shortest possible version, I think.

    checkBoxColumn.setCellValueFactory(c -> {
        c.getValue().booleanProperty().addListener((ch, o, n) -> {
        // do something
        });
        return c.getValue().booleanProperty();
    });
    checkBoxColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkBoxColumn));
    
    0 讨论(0)
  • 2020-11-30 01:12

    Small and simple.

    row.setCellValueFactory(c -> new SimpleBooleanProperty(c.getValue().getIsDefault()));
    row.setCellFactory(tc -> new CheckBoxTableCell<>());

    0 讨论(0)
提交回复
热议问题