Multiple Components in one column of JavaFX TableView


Question

I am working with JavaFx 2.2. I am having a problem that I am not able to place different components in a TableView Column. For example I have two columns

1) Answer

2) AnswerType

If AnswerType contains “Multiple Choice” then the corresponding cell in Answer Column should display a ComboBox else it should display a TextField.

I have a code example below but its displaying either ComboBox or TextField but not both in different cells of same column.

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.scene.control.cell.ComboBoxTableCell;

public class TableCellWithMultipleComponent extends Application {

     @SuppressWarnings("rawtypes")
TableColumn answerTypeCol; 
@SuppressWarnings("rawtypes")
TableColumn answerCol; 
ObservableList<String> namesChoiceList;

public static void main(String[] args) {
    launch(args);
}

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void start(final Stage primaryStage) {
    primaryStage.setTitle("Table Cell With Multiple Components");

     TableView<Person> table = new TableView<Person>();
     table.setEditable(true);
      final ObservableList<Person> data = 
                FXCollections.observableArrayList(
                    new Person("A", "Multiple Choice"),
                    new Person("JOHN", "Free Text"),
                    new Person("123", "Free Text"),
                    new Person("D", "Multiple Choice")
                );

    GridPane gridpane = new GridPane();
    gridpane.setPadding(new Insets(5));
    gridpane.setHgap(5);
    gridpane.setVgap(5);


    namesChoiceList = FXCollections.observableArrayList("A", "B", "C", "D", "INVALID_ANSWER", "NO_ANSWER");

    answerCol = new TableColumn();
    answerCol.setText("Answers");
    answerCol.setMinWidth(210);
    answerCol.setEditable(true);
    answerCol.setCellValueFactory(new PropertyValueFactory("answers"));


    answerCol.setCellFactory( new Callback<TableColumn<String, String>, TableCell<String, String>>() {
        @Override
        public TableCell<String, String> call(TableColumn<String, String> arg0) {
            return new anyMethod();
        }
    });



    answerTypeCol = new TableColumn();
    answerTypeCol.setText("Answers Type");
    answerTypeCol.setMinWidth(210);
    answerTypeCol.setEditable(true);
    answerTypeCol.setCellValueFactory(new PropertyValueFactory("answersType"));



    table.setItems(data);
    table.getColumns().addAll(answerCol, answerTypeCol);

    StackPane root = new StackPane();

    Scene scene =new Scene(root, 500, 550);

    gridpane.add(table, 1, 5,1,20 );


    root.getChildren().addAll(gridpane);
    primaryStage.setScene(scene);
    primaryStage.show();

   }


  private class anyMethod extends TableCell <String, String>{


    @SuppressWarnings("unchecked")
    @Override
    protected void updateItem(String item, boolean arg1) {
        super.updateItem(item, arg1);

        answerCol.setCellFactory(ComboBoxTableCell.<String, String>forTableColumn(namesChoiceList));

        /****  I have to execute this commented code so that if the column cell has text "Multiple Choice" then
         * it displays the comboBox otherwise it displays the text field in the Table View cell

        if (item.equalsIgnoreCase("Multiple Choice")){
            answerCol.setCellFactory(ComboBoxTableCell.<String, String>forTableColumn(namesChoiceList));
        }
        else{
            //answerCol.setCellFactory(TextFieldTableCell.<String>forTableColumn());
        }
    ****/
    }

}


public static class Person {
    private final SimpleStringProperty answers;
    private final SimpleStringProperty answersType;


    private Person(String answers, String answersType) {
        this.answers = new SimpleStringProperty(answers);
        this.answersType = new SimpleStringProperty(answersType);
    }

    public String getAnswers() {
        return answers.get();
    }
    public void setAnswers(String answers) {
        this.answers.set(answers);
    }

    public String getAnswersType() {
        return answersType.get();
    }
    public void setAnswersType(String answersType) {
        this.answersType.set(answersType);
    }
   }



}
1
4
12/13/2012 5:36:50 AM

Accepted Answer

Here is a sample for an EditingCell which renders a different control in the cell (TextEdit field or Checkbox) depending on the type of data represented by the Cell's backing field (String or Boolean). Complete executable code is available as a gist.

For your particular example, use the same concept except query the type for either a String => TextField or ObservableList => combobox. Also, for your particular sample, ChoiceBox may be a simpler control to use than ComboBox.

class EditingCell extends TableCell<NamedProperty, Object> {
private TextField textField;
private CheckBox checkBox;
public EditingCell() {}

@Override public void startEdit() {
  if (!isEmpty()) {
    super.startEdit();

    if (getItem() instanceof Boolean) {
      createCheckBox();
      setText(null);
      setGraphic(checkBox);
    } else {
      createTextField();
      setText(null);
      setGraphic(textField);
      textField.selectAll();
    }  
  }
}

@Override public void cancelEdit() {
  super.cancelEdit();

  if (getItem() instanceof Boolean) {
    setText(getItem().toString());
  } else {
    setText((String) getItem());
  }  
  setGraphic(null);
}

@Override public void updateItem(Object item, boolean empty) {
  super.updateItem(item, empty);

  if (empty) {
    setText(null);
    setGraphic(null);
  } else {
    if (isEditing()) {
      if (getItem() instanceof Boolean) {
        if (checkBox != null) {
          checkBox.setSelected(getBoolean());
        }
        setText(null);
        setGraphic(checkBox);
      } else {
        if (textField != null) {
          textField.setText(getString());
        }
        setText(null);
        setGraphic(textField);
      }  
    } else {
      setText(getString());
      setGraphic(null);
    }
  }
}

private void createTextField() {
  textField = new TextField(getString());
  textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
  textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
    @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
      if (!newValue) {
        commitEdit(textField.getText());
      }
    }
  });
}

private void createCheckBox() {
  checkBox = new CheckBox();
  checkBox.setSelected(getBoolean());
  checkBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
  checkBox.focusedProperty().addListener(new ChangeListener<Boolean>() {
    @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
      if (!newValue) {
        commitEdit(checkBox.isSelected());
      }
    }
  });
}

private String getString() {
  return getItem() == null ? "" : getItem().toString();
}

private Boolean getBoolean() {
  return getItem() == null ? false : (Boolean) getItem();
}
}  
6
12/13/2012 6:22:02 PM

Bellow is the working code for my question. Thank jewelsea

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.TableCell;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.control.TextField;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    import javafx.util.Callback;

    public class TableCellWithMultipleComponent extends Application {
        @SuppressWarnings("rawtypes")
        TableColumn answerTypeCol; 
        @SuppressWarnings("rawtypes")
        TableColumn answerCol; 
        ObservableList<String> namesChoiceList;
        @SuppressWarnings("rawtypes")
        ComboBox comboBox;
        TextField textField;

        public static void main(String[] args) {
            launch(args);
        }

        @SuppressWarnings({ "rawtypes", "unchecked" })
        @Override
        public void start(final Stage primaryStage) {
            primaryStage.setTitle("Table Cell With Multiple Components");

             TableView<Person> table = new TableView<Person>();
             table.setEditable(true);
              final ObservableList<Person> data = 
                        FXCollections.observableArrayList(
                            new Person("A", "Multiple Choice"),
                            new Person("JOHN", "Free Text"),
                            new Person("123", "Free Text"),
                            new Person("D", "Multiple Choice")
                        );



            GridPane gridpane = new GridPane();
            gridpane.setPadding(new Insets(5));
            gridpane.setHgap(5);
            gridpane.setVgap(5);


            namesChoiceList = FXCollections.observableArrayList("A", "B", "C", "D", "INVALID_ANSWER", "NO_ANSWER");

            answerCol = new TableColumn();
            answerCol.setText("Answers");
            answerCol.setMinWidth(210);
            answerCol.setEditable(true);
            answerCol.setCellValueFactory(new PropertyValueFactory("answers"));


            answerCol.setCellFactory( new Callback<TableColumn<String, String>, TableCell<String, String>>() {
                @Override
                public TableCell<String, String> call(TableColumn<String, String> arg0) {
                    return new anyMethod();
                }
            });



            answerTypeCol = new TableColumn();
            answerTypeCol.setText("Answers Type");
            answerTypeCol.setMinWidth(210);
            answerTypeCol.setEditable(true);
            answerTypeCol.setCellValueFactory(new PropertyValueFactory("answersType"));



            table.setItems(data);
            table.getColumns().addAll(answerCol, answerTypeCol);

            StackPane root = new StackPane();

            Scene scene =new Scene(root, 500, 550);

            gridpane.add(table, 1, 5,1,20 );


            root.getChildren().addAll(gridpane);
            primaryStage.setScene(scene);
            primaryStage.show();


       }


        private class anyMethod extends TableCell <String, String>{

            @SuppressWarnings({ "unchecked", "rawtypes" })
            public anyMethod(){

                comboBox = new ComboBox();
                textField = new TextField();
                comboBox.setItems(namesChoiceList);
            }

            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                     if (empty) {
                    setText(null);
                   setGraphic(null);
                    System.out.println("In empty");
                 } else {
                    if( getTableView().getColumns().get(1).getCellData(getIndex()).toString().startsWith("M")){

                     System.out.println("Making ComboBox");
                     setGraphic(comboBox);
                    }
                    else{
                        setGraphic(textField);
                    }
                 }

            }

        }


        public static class Person {
            private final SimpleStringProperty answers;
            private final SimpleStringProperty answersType;


            private Person(String answers, String answersType) {
                this.answers = new SimpleStringProperty(answers);
                this.answersType = new SimpleStringProperty(answersType);
            }

            public String getAnswers() {
                return answers.get();
            }
            public void setAnswers(String answers) {
                this.answers.set(answers);
            }

            public String getAnswersType() {
                return answersType.get();
            }
            public void setAnswersType(String answersType) {
                this.answersType.set(answersType);
            }
        }



    }

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon