I've searched for a while now explanations on internet about filling cells of a TableView
with ComboBox
, but each one of them is using lines such as :
TableView<SomeObject> tableView = new TableView<>();
TableColumn<SomeObject, String> column = new TableColumn<>("property");
ObservableList<String> list = FXCollections.observableArrayList("option1", "option2", "option3");
column.setCellValueFactory(new PropertyValueFactory<>("property"));
column.setCellFactory(ComboBoxTableCell.forTableColumn(list);
tableView.getColumns().add(column);
... with SomeObject
defined in another class.
The point is, I'm building a GUI where I cannot define Object to fill any TableView
(because I can't anticipate the number of columns my table will contain). All I know is my TableView
will be full of String
, and the first two columns will always have the same title ("N°" and "Truth"). Nothing else. So I cannot use the upper code because I don't have any "property"
to fill.
Here is how I fill my TableView
, without ComboBox
, (and it works correctly), where my data is called rawData
and it's an Partionner
(code below) :
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
for(int i = 1; i < rawData.size(); i++){
// Begin at i = 1 because the first raw (i = 0) is the names of the columns.
data.add(FXCollections.observableArrayList(rawData.get(i)));
}
tableView.setItems(data);
for(int i = 0; rawData.get(0).size(); i++){
final int cuurentColumn = i;
TableColumn<ObservableList<String>, String> column = new TableColumn<>((String) rawData.get(0).get(i)); // Here are the columns titles.
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().get(currentColumn)));
column.setStyle("-fx-alignment: CENTER");
tableView.getColumns().add(column);
}
where a Partionner
is :
public final class Partionner<T> extends AbstractList<List<T>>{
private final List<T> list;
private final int chunkSize;
public Partionner(List<T> list, int chunkSize){
this.list = list;
this.chunkSize = chunkSize;
}
public static <T> Partitionner<T> ofSize(List<T> list, int chunkSize){
return new Partionner<>(list, chunkSize);
}
@Override
public List<T> get(int index){
int start = index * chunkSize;
int end = Math.min(start + chunkSize, list.size());
if(start > end){
throw new IndexOutOfBoundsException("OOB Partitionner");
}
return new ArrayList<>(list.subList(start, end));
}
public int size(){
return (int) Math.ceil((double) list.size() / (double) chunkSize);
}
// You can create a Partionner with :
Partitionner partitionner = Partitionner.ofSize(arrayList, length);
// Where arraylist is an ArrayList<String> and length it's size
}
From all this data, which come from a CSV actually, I want to use a comboBox for each cell of the 2nd column "Truth". I thought about something like :
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
for(int i = 1; i < rawData.size(); i++){
// Begin at i = 1 because the first raw (i = 0) is the names of the columns.
data.add(FXCollections.observableArrayList(rawData.get(i)));
}
tableView.setItems(data);
for(int i = 0; i < rawData.get(0).size(); i++){
final int currentColumn = i;
if(i == 1){
// So it's here I'm completly disabled to fill this code :/
// Begin of bullshit
TableColumn<ObservableList<String>, String> column = new TableColumn<>((String) rawData.get(0).get(i));
ObservableList<String> possibleTruth = FXCollections.obervableArrayList("1", "2", "3");
final ComboBox<String> comboBox = new ComboBox<>(possibleTruth);
column.setCellValueFactory(......);
column.setCellFactory(......);
tableView.getColumns.add(col);
// End of bullshit
} else {
TableColumn<ObservableList<String>, String> column = new TableColumn<>((String) rawData.get(0).get(i));
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().get(currentColumn)));
column.setStyle("-fx-alignment: CENTER");
tableView.getColumns().add(column);
}
}
Any ideas to fill my code ? Feel free to leave your paypal if your solution works haha (jk) :)
----------- Here is a minimal reproductible example -----------
Class main :
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setResizable(false);
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Class Controller :
public class Controller {
@FXML public TableView tableView;
public ArrayList<String> inputData;
public Partitionner rawData;
public void initialize(){
inputData = new ArrayList<>();
inputData.add("N°");
inputData.add("Truth");
inputData.add("Col A");
inputData.add("Col B");
inputData.add("1");
inputData.add("t1");
inputData.add("valA1");
inputData.add("valB1");
inputData.add("2");
inputData.add("t2");
inputData.add("valA2");
inputData.add("valB2");
inputData.add("3");
inputData.add("t3");
inputData.add("valA3");
inputData.add("valB3");
rawData = Partitionner.ofSize(inputData, 4);
System.out.println(rawData);
}
public void populateTableView (){
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
for(int i = 1; i < rawData.size(); i++){
// Begin at i = 1 because the first raw (i = 0) is the names of the columns.
data.add(FXCollections.observableArrayList(rawData.get(i)));
}
tableView.setItems(data);
for(int i = 0; i < rawData.get(0).size(); i++){
final int currentColumn = i;
if(i == 1){
// Trying to fill the "Truth" column with a combobox in each cell containing t1, t2 and t3.
TableColumn<ObservableList<String>, String> column = new TableColumn<>((String) rawData.get(0).get(i));
ObservableList<String> possibleTruth = FXCollections.observableArrayList("t1", "t2", "t3");
column.setCellFactory(ComboBoxTableCell.forTableColumn(possibleTruth));
tableView.getColumns().add(column);
} else {
TableColumn<ObservableList<String>, String> column = new TableColumn<>((String) rawData.get(0).get(i));
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().get(currentColumn)));
column.setStyle("-fx-alignment: CENTER");
tableView.getColumns().add(column);
}
}
}
}
sample.fxml file :
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="752.0" prefWidth="1162.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<Button layoutX="140.0" layoutY="683.0" mnemonicParsing="false" onAction="#populateTableView" prefHeight="25.0" prefWidth="161.0" text="Fill Data" />
<TableView fx:id="tableView" layoutX="43.0" layoutY="39.0" prefHeight="585.0" prefWidth="1073.0" />
</children>
</AnchorPane>
And don't forget to add the Partitionner
class, which is just above.