4 Replies Latest reply: Dec 11, 2012 2:46 PM by eno g. - oracle RSS

    Help with tableview/column strategy

    eno g. - oracle
      I'm greener than a newbie at this so bare with me :)

      I somehow had the following scenario working in Swing, but decided to play with JavaFX instead. With that said, here's the scenario:

      I have a delimited text files that I want to display on a table. The file may have a different number of columns as below. As you can see some of the fields are empty so I can't just put the fields in a list/array just by splitting the line,
      as the values would end up in the wrong columns. Anyhow, I have a class that will figure out what goes where and build a String[] with the correct values and create an object <RowLine> that contains this array.


      Column1 Column2 Column3 Column4 Column5
      row1 value1 value2 value3 value4 value5
      row2 value1 value2 NULL value4 value5
      row3 value1 NULL value3 NULL value5


      in Swing I was able to just add a new row to the JTable by extracting the Array elements of RowLine object.

      In JavaFX I'm confused as to what I should use as an ObservableList<Something> data to display in the table.

      Should I create an ObservableList<RowLine> and use that? I'm not sure how I would create the TableColumn and assign it the correct String value from inside the RowLine object?
        • 1. Re: Help with tableview/column strategy
          James_D
          In your example, every row has five columns; it's just that some rows have missing values in some of their columns. If this is always true, then it should be pretty easy. Even if not, it's only a little more complex.

          If you have a class (RowLine) that represents a single row (i.e. each row is represented by an instance of that class), then that's good. I'm assuming RowLine has either a method
          public String getValue(int column);
          returning the value for the specified column, or at least a
          public String[] getValues();
          method (so you can call rowline.getValues()[index] to get the same effect).

          So if all these conditions hold, you can make the your table view with RowLine as its data type:
          final TableView<RowLine> table = new TableView<RowLine>();
          and then create the columns:
          final int numCols = 5 ; // or compute it however you need
          for (int colIndex = 0 ; colIndex < numCols ; colIndex++) {
            table.getColumns().add(createColumn(colIndex));
          }
          The createColumn(int index) method can be defined as follows:
          private TableColumn<RowLine, String> createColumn(final int index) {
            TableColumn<RowLine, String> column = new TableColumn<RowLine, String>("Column "+(index+1));
            column.setCellValueFactory(new Callback<CellDataFeatures<RowLine, String>, ObservableValue<String>>() {
              @Override
              public ObservableValue<String> call(CellDataFeatures<RowLine, String> cellDataFeatures) {
                RowLine line = cellDataFeatures.getValue(); // the RowLine instance for a given row
                String value = line.getValue(index); // retrieve the value for this column from the row line
                // String value = line.getValues()[index]; // depending on the methods defined in RowLine
                if (value==null) value = "" ;
                return new SimpleStringProperty(value);
              }
            });
            return column ;
          }
          To populate the table with your data, you presumably have a method which reads your file and creates a List<RowLine> or something similar.
          Then you can do
          List<RowLine> data = readFile();
          table.setItems(FXCollections.observableArrayList(data));
          If you want to add each row to the table as you read it from the file, you can do something like
          BufferedReader in = // create the BufferedReader to read from your file
          String line ;
          while ((line = in.readLine()) !=null) {
            final RowLine rowLine = processLine(line);
            table.getItems().add(rowLine);
          }
          ...
          but you should think a bit about whether you really want to be reading a potentially large file in the JavaFX Application Thread. (I'm assuming a method processLine(String line) here that reads the line and creates the RowLine object from it.) If you execute that loop in an application thread you should replace table.getItems().add(rowLine) with
          Platform.runLater(new Runnable() {
            @Override
            public void run() {
              table.getItems().add(rowLine);
            }
          });
          Now, if your data really does have a variable number of columns, you'll need to add additional columns to the table when you read a line that has more data than the current number of columns. I'll assume RowLine has a getValueCount() method that just returns the size of the String array it wraps, and also that you're reading the file in a separate thread. Then remove the for loop above which calls table.getColumns().add(...) and do this when you read the data:
          BufferedReader in = // create the BufferedReader to read from your file
          String line ;
          while ((line = in.readLine()) !=null) {
            final RowLine rowLine = processLine(line);
            Platform.runLater(new Runnable() {
              @Override
              public void run() {
                for (int index = table.getColumns().size(); index < rowLine.getValueCount(); index++) {
                  table.getColumns().add(createColumn(index));
                }
                table.getItems().add(rowLine);
              }
            });
          }
          ...
          In this scenario, you must make sure that RowLine.getValue(int index) returns null if index is greater than or equal to the length of the array of values it wraps (else you'll get ArrayIndexOutOfBoundsExceptions all over the place).

          You could also read the lines into a list, and keep track of the largest line as you do, then build the columns in a simple for loop as before.


          Caveat: this code was not even run through a compiler, so there's likely typos in here which will need fixing; but you did just ask for a strategy :).
          • 2. Re: Help with tableview/column strategy
            eno g. - oracle
            Thanks again sir!

            Yes all of the above were true. I just couldn't figure out how to createColumn and assign value from my Row object.
            • 3. Re: Help with tableview/column strategy
              twasyl
              Hi there,

              I've written a blog post that may be help you: http://thierrywasyl.wordpress.com/2012/11/30/tableview-display-tables-in-javafx/

              Cheers.
              • 4. Re: Help with tableview/column strategy
                eno g. - oracle
                Thanks Thierry, that's useful info!