Table with buttons using FXML (JavaFX)


Question

I am learning JavaFX and FXML. I am using the tutorials available on the Oracle website.

In particular, I am developing an application using the Oracle example of an FXML Address Book as a starting point (https://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm#CACFEHBI). What I want to do now, is to add a column with a button associated with each entry of the phone book. For example, a button that when clicked shows, on another table, more data regarding that name entry. I want to do this using FXML. Searching the web I found some resources on how to create a button column, but they look kind of confusing and it seems they don't use FXML, which is what I need.

Anybody can suggest me some tutorial or hint?

Edit: I started my work following this tutorial (https://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm#CACFEHBI). Part of what I want to do, is described here: JavaFX table- how to add components?. However, this example doesn't make use of fxml. What I am trying to do is mixing the codes. So, I have a fxml file (taken from the oracle tutorial linked above) where the table is declared, this way:

    <TableView fx:id="tableView" GridPane.columnIndex="0" 
    GridPane.rowIndex="0">
    <columns>
        <TableColumn fx:id="Names_column" text="Table" prefWidth="100">
            <cellValueFactory><PropertyValueFactory property="name" />
            </cellValueFactory>
            <cellFactory>
                <FormattedTableCellFactory alignment="center">                        
                </FormattedTableCellFactory>
            </cellFactory>
        </TableColumn>

        <TableColumn text="Color" prefWidth="100">
            <cellValueFactory><PropertyValueFactory property="color" />
            </cellValueFactory>                
        </TableColumn>  

        <TableColumn text="Button" prefWidth="100">          
        </TableColumn>                                  
    </columns>

    <items>
        <FXCollections fx:factory="observableArrayList">
            <ColTab name="Table 1" color="1"   />  
            <ColTab name="Table 2" color="2"   />
            <ColTab name="Table 3" color"1"   />
        </FXCollections>
    </items>

</TableView>

Then, I took a look at the other example (JavaFX table- how to add components?). In this example, everything is made only using Java, but for my purpose, I need to use fxml. In my .java main file, I did this:

       TableColumn<ColTab, ColTab> btnCol = new TableColumn<>("pulsanti");
   btnCol.setMinWidth(150);
   btnCol.setCellValueFactory(new Callback<CellDataFeatures<ColTab, ColTab>, ObservableValue<ColTab>>() {
      @Override public ObservableValue<ColTab> call(CellDataFeatures<ColTab, ColTab> features) {
          return new ReadOnlyObjectWrapper(features.getValue());     
      }
    });

    btnCol.setCellFactory(new Callback<TableColumn<ColTab, ColTab>, TableCell<ColTab, ColTab>>() {
      @Override public TableCell<ColTab, ColTab> call(TableColumn<ColTab, ColTab> btnCol) {
        return new TableCell<ColTab, ColTab>() {              
          final Button button = new Button(); {              
            button.setMinWidth(130);
          }
          @Override public void updateItem(final ColTab ColTab, boolean empty) {
            super.updateItem(ColTab, empty);
            if (ColTab != null) {
                  button.setText("Do something");

              button.setOnAction(new EventHandler<ActionEvent>() {
                @Override public void handle(ActionEvent event) {
                  //do something
                }
              });
            }
          }
        };
      }
    });

    //table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, btnCol);

This code is copied from the second example I linked, and actually is not too clear to me. What I see is that it creates a new column and sets some properties. The last line of code (commented out) adds this column, and some other ones, to a table, which was previously declared in the original .java file and which I didn't declare, since I already declared the table in the fxml file. Now I am looking for a way to integrate this column that I am defining in the .java file, in the table that I declare in my .fxml file. I know that what i am saying might look very messy, but I am really new to JavaFX and fxml, and not all the details are clear to me.

Edit2: My code for the controller looks like this:

public class FXMLDocumentController implements Initializable {

@FXML
private TableView<ColTab> tableView;    
tableView.getColumns().add(btnCol); 


@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}    

}

Now I am getting an error that says:

package tableView does not exist

identifier expected

';' expected

I am using Netbeasn as ide.

1
1
5/23/2017 12:10:08 PM

You can just put the code you copied into the initialize method of your controller. You have already defined an fx:id=tableView attribute on your table, so I assume you have

@FXML
private TableView<ColTab> tableView ;

in the controller. Just add the extra column to the table view with

tableView.getColumns().add(btnCol);

To understand how this column is being configured, you need to understand that there is a separation between the data displayed in the table, and the view of the data (i.e. the presentation of the data to the user). In JavaFX, for complex UI controls such as tables, trees, and lists, the view of the data is represented by a cell. The cell factory that you set on a column tells JavaFX how to create a TableCell from the data displayed in the cell - it's essentially a function that creates TableCells when needed. The cell's updateItem(..) method is called when the cell is first created, and any time the data changes.

The logic in your updateItem(...) method is not quite correct. First, you need to actually display the button in the cell, by calling the cell's setGraphic(...) method. Second, you do need to handle the case where the cell is empty (i.e. the item is null). So I would do something like this:

btnCol.setCellFactory(new Callback<TableColumn<ColTab, ColTab>, TableCell<ColTab, ColTab>>() {
  @Override public TableCell<ColTab, ColTab> call(TableColumn<ColTab, ColTab> btnCol) {
    final Button button = new Button();
    button.setMinWidth(130);
    TableCell<ColTab, ColTab> cell = new TableCell<ColTab, ColTab>() {              
      @Override public void updateItem(final ColTab ColTab, boolean empty) {
        super.updateItem(ColTab, empty);
        if (ColTab == null) {
            setGraphic(null);
        } else {
            button.setText("Do something");
            setGraphic(button);
        }

      }
    };

    button.setOnAction(new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent event) {
          ColTab currentItem = cell.getItem();
          //do something with current item...
        }
    });

    return cell ;
  }
});

You can read more about the cell mechanism in general in the Cell Javadocs and the TableView Javadocs. The tutorial for ListView and TableView may also help.

The cell value factory tells the table how to get the data for a particular cell from the data for that row. In this case this is a bit subtle. The button depends on the data for the whole row (because it will need those data in its action handler), so it just returns a wrapper around the row data object. The TableColumn Javadocs and the table tutorial linked above talk about this.

1
11/16/2014 3:19:05 PM

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