JavaFX: Trabalhando com TableView

Para trabalhar com tabelas em JavaFX utilizamos as classes TableView, TableColumn e várias outras classes auxiliares. Essas classes oferecem diversos recursos e muita flexibilidade na hora de construir uma tabela, mas também podem ser um pouco complexas de se trabalhar a depender da necessidade da sua aplicação. Vejamos alguns casos não triviais, como a inclusão de Checkbox e TextField nas células, e a construção de colunas aninhadas.


Criando a Tabela

Vamos criar uma tabela para exibir informações de uma entidade Cliente em uma aplicação fictícia. A tabela possui inicialmente quatro colunas: “Nome”, “Idade”, “Endereço”, e uma coluna com checkbox para permitir a seleção de um registro.

A classe principal da aplicação é a TableViewApp, que inicia o JavaFX, carrega o FXML e exibe a janela:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TableViewApp extends Application{
public static void main(String[] args) {
launch(TableViewApp.class);
}

@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(
getClass().getResource("app.fxml"));
Scene scene = new Scene(root, 600, 400);
stage.setScene(scene);
stage.setTitle("TableView App");
stage.show();
}
}

O arquivo app.fxml define a tabela com 4 colunas:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="AppController">

<padding>
<Insets top="20" bottom="20" left="20" right="20" />
</padding>

<TableView>
<columns>
<TableColumn text=""></TableColumn>
<TableColumn text="Nome"></TableColumn>
<TableColumn text="Idade"></TableColumn>
<TableColumn text="Endereço"></TableColumn>
</columns>
</TableView>
</VBox>

Executando a aplicação vemos a seguinte janela:


Ajustando a largura das colunas

Na interface atual, todas as colunas tem a mesma largura e elas não ocupam todo o espaço disponível na tabela. Para corrigir isso, primeiro vamos fazer com que as colunas ocupem todo o espaço disponível na tabela, modificando o Resize Policy (Política de Redimensionamento) da tabela para constrained:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml">

<padding>
<Insets top="20" bottom="20" left="20" right="20" />
</padding>

<TableView>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>

<columns>
<TableColumn text=""/>
<TableColumn text="Nome"/>
<TableColumn text="Idade"/>
<TableColumn text="Endereço"/>
</columns>
</TableView>
</VBox>

Com essa modificação, as colunas passam a ocupar toda a largura da TableView, mesmo que a janela seja redimensionada:

Podemos então configurar a largura de algumas colunas. Por exemplo, a primeira coluna (que não tem título) vai conter apenas o checkbox, portanto ela ficaria melhor se ocupasse menos espaço. A coluna “Idade” vai exibir apenas um valor numérico e também pode ser menor. Podemos forçar essas colunas a terem um tamanho fixo ao setar as propriedades minWidth e maxWidth para o mesmo valor:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml">

<padding>
<Insets top="20" bottom="20" left="20" right="20"/>
</padding>

<TableView>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
<columns>
<TableColumn text="" minWidth="40" maxWidth="40"/>
<TableColumn text="Nome"/>
<TableColumn text="Idade" minWidth="70" maxWidth="70"/>
<TableColumn text="Endereço"/>
</columns>
</TableView>
</VBox>

Após essa alteração, a tabela passa a distribuir melhor a largura para as colunas “Nome” e “Endereço”:


Criando o controller

Para popular os dados da tabela, primeiro precisamos criar um controller para a view app.fxml. No controller, vamos precisar referenciar a tabela e suas colunas para manipulá-los como objetos Java. Para isso, utilizamos a annotation @FXML no controller, e o atributo fx:id no fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="AppController">

<padding>
<Insets top="20" bottom="20" left="20" right="20"/>
</padding>

<TableView fx:id="tabela">
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
<columns>
<TableColumn fx:id="selectCol"
text=""
minWidth="40"
maxWidth="40"/>
<TableColumn fx:id="nomeCol"
text="Nome"/>
<TableColumn fx:id="idadeCol"
text="Idade"
minWidth="70"
maxWidth="70"/>
<TableColumn fx:id="enderecoCol"
text="Endereço"/>
</columns>
</TableView>
</VBox>

E o controller:

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

public class AppController {
@FXML
private TableView tabela;
@FXML
private TableColumn selectCol;
@FXML
private TableColumn nomeCol;
@FXML
private TableColumn idadeCol;
@FXML
private TableColumn enderecoCol;

}

Criando a classe de modelo

Antes de preencher a tabela, é necessário criar uma classe de modelo para representar os dados da tabela. Vamos criar a classe Cliente para esse fim. Por praticidade, ela será uma classe estática dentro do controller. Cada atributo dessa classe corresponde a uma coluna da tabela, e cada instância dessa classe correponde a uma linha (registro) na tabela:

public static class Cliente {
private final SimpleBooleanProperty selected;
private final SimpleStringProperty nome;
private final SimpleIntegerProperty idade;
private final SimpleStringProperty endereco;

public Cliente(String nome, Integer idade, String endereco) {
this.selected = new SimpleBooleanProperty(false);
this.nome = new SimpleStringProperty(nome);
this.idade = new SimpleIntegerProperty(idade);
this.endereco = new SimpleStringProperty(endereco);
}

public boolean isSelected() {
return selected.get();
}

public SimpleBooleanProperty selectedProperty() {
return selected;
}

public void setSelected(boolean selected) {
this.selected.set(selected);
}

public String getNome() {
return nome.get();
}

public SimpleStringProperty nomeProperty() {
return nome;
}

public void setNome(String nome) {
this.nome.set(nome);
}

public int getIdade() {
return idade.get();
}

public SimpleIntegerProperty idadeProperty() {
return idade;
}

public void setIdade(int idade) {
this.idade.set(idade);
}

public String getEndereco() {
return endereco.get();
}

public SimpleStringProperty enderecoProperty() {
return endereco;
}

public void setEndereco(String endereco) {
this.endereco.set(endereco);
}
}

Observe que os atributos são do tipo SimpleBooleanProperty (para a coluna do checkbox, que guarda um valor booleano), SimpleStringProperty (para as colunas “Nome” e “Endereço”, que são Strings) e SimpleIntegerProperty (para a coluna “Idade”, que é um inteiro). Criamos também um construtor e os métodos get e set das propriedades.

Com a criação da classe de modelo, vamos aproveitar e definir também os argumentos de tipo nas instâncias da tabela e das colunas no controller. Isso é útil porque as classes TableView e TableColumn são tipos genéricos do Java, e declarando os argumentos de tipo nós ganhamos um pouco mais de segurança de tipo (type safety) do compilador:

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

public class AppController {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;

// definição da classe Cliente...
}

Associando as colunas com os dados do modelo

Precisamos associar cada coluna da tabela a um atributo da classe Cliente. Primeiramente, vamos fazer o controller implementar a interface Initializable, permitindo assim que possamos executar algumas configurações durante a inicialização da interface, no método initialize():

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

import java.net.URL;
import java.util.ResourceBundle;

public class AppController implements Initializable {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;

@Override
public void initialize(URL location, ResourceBundle resources) {
// inicialização vem aqui...
}


// declaração da classe Cliente...
}

Durante a inicialização, vamos associar as colunas com os respectivos atributos da classe Cliente. Fazemos isso ao definir uma factory para os valores da coluna. O JavaFX já fornece uma factory básica para produzir os valores exibidos na coluna com base nos valores de uma propriedade da classe de modelo, a PropertyValueFactory:

iimport javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

import java.net.URL;
import java.util.ResourceBundle;

public class AppController implements Initializable {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;

@Override
public void initialize(URL location, ResourceBundle resources) {
selectCol.setCellValueFactory(
new PropertyValueFactory<>("selected"));
nomeCol.setCellValueFactory(
new PropertyValueFactory<>("nome"));
idadeCol.setCellValueFactory(
new PropertyValueFactory<>("idade"));
enderecoCol.setCellValueFactory(
new PropertyValueFactory<>("endereco"));

}

// definição da classe Cliente...
}

Preenchendo a tabela

Para preencher a tabela, precisamos criar uma ObservableList contendo as instancias de Cliente que queremos exibir, e passar essa lista para a TableView com o método setItems():

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

import java.net.URL;
import java.util.ResourceBundle;

public class AppController implements Initializable {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;

@Override
public void initialize(URL location, ResourceBundle resources) {
selectCol.setCellValueFactory(
new PropertyValueFactory<>("selected"));
nomeCol.setCellValueFactory(
new PropertyValueFactory<>("nome"));
idadeCol.setCellValueFactory(
new PropertyValueFactory<>("idade"));
enderecoCol.setCellValueFactory(
new PropertyValueFactory<>("endereco"));

tabela.setItems(listaDeClientes());
}

private ObservableList<Cliente> listaDeClientes() {
return FXCollections.observableArrayList(
new Cliente("Antonio", 28, "Rua Alvenaria 22"),
new Cliente("Bruno", 19, "Rua São Domingos 108"),
new Cliente("Manoel", 45, "Rua Valentim 05"),
new Cliente("Cassandra", 33, "Rua Palmeira 234"),
new Cliente("Roberto", 69, "Rua Jean Nassif 56"),
new Cliente("Mariana", 16, "Av Rendeiras 78")
);
}


// definição da classe Cliente...
}

Com isso, a tabela passa a exibir os dados dos clientes:


Exibindo o checkbox na TableView

No último screenshot, a primeira coluna está exibindo o valor booleano da propriedade “selected” da classe Cliente. Mas a nossa intenção é que seja exibido um checkbox nessa coluna, para que o usuário possa marcar ou desmarcar o checkbox e essa marcação se reflita no valor booleano do atributo “selected”.

Para fazer com que a coluna exiba um checkbox, precisamos definir uma factory para as células da coluna. Anteriormente, definimos uma factory para criar os valores das células da coluna com o método setCellValueFactory(). Agora precisamos definir uma factory para criar as células propriamente ditas, ou seja, definir a maneira como as células são renderizadas. Fazemos isso com o método setCellFactory():

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;

import java.net.URL;
import java.util.ResourceBundle;

public class AppController implements Initializable {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;

@Override
public void initialize(URL location, ResourceBundle resources) {
selectCol.setCellValueFactory(
new PropertyValueFactory<>("selected"));
nomeCol.setCellValueFactory(
new PropertyValueFactory<>("nome"));
idadeCol.setCellValueFactory(
new PropertyValueFactory<>("idade"));
enderecoCol.setCellValueFactory(
new PropertyValueFactory<>("endereco"));

selectCol.setCellFactory(
CheckBoxTableCell.forTableColumn(selectCol));


tabela.setItems(listaDeClientes());
}

private ObservableList<Cliente> listaDeClientes() {
return FXCollections.observableArrayList(
new Cliente("Antonio", 28, "Rua Alvenaria 22"),
new Cliente("Bruno", 19, "Rua São Domingos 108"),
new Cliente("Manoel", 45, "Rua Valentim 05"),
new Cliente("Cassandra", 33, "Rua Palmeira 234"),
new Cliente("Roberto", 69, "Rua Jean Nassif 56"),
new Cliente("Mariana", 16, "Av Rendeiras 78")
);
}

// definição da classe Cliente...
}

Note que para obter a factory que queremos, utilizamos um método estático já fornecido pela classe CheckBoxTableCell. Além de definir a factory, também precisamos configurar a tabela como editável. Podemos fazer isso no fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="AppController">

<padding>
<Insets top="20" bottom="20" left="20" right="20"/>
</padding>

<TableView fx:id="tabela" editable="true">
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
<columns>
<TableColumn fx:id="selectCol"
text=""
minWidth="40"
maxWidth="40"/>
<TableColumn fx:id="nomeCol"
text="Nome"/>
<TableColumn fx:id="idadeCol"
text="Idade"
minWidth="70"
maxWidth="70"/>
<TableColumn fx:id="enderecoCol"
text="Endereço"/>
</columns>
</TableView>
</VBox>

O resultado é mostrado a seguir:


Tornando a coluna “Nome” editável

Vamos tornar a coluna “Nome” editável, para que o usuário possa modificar o nome que é exibido na tabela. Para isso, além de configurar o atributo editable=“true” da TableView (que fizemos no último passo) precisamos definir uma factory para as células da coluna “Nome”, assim como fizemos com a coluna do checkbox. Essa factory deve exibir as células da coluna como TextField’s.

Assim como fizemos para a coluna do checkbox, podemos utilizar um método estático da classe TextFieldTableCell para obter essa factory específica:

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;

import java.net.URL;
import java.util.ResourceBundle;

public class AppController implements Initializable {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;

@Override
public void initialize(URL location, ResourceBundle resources) {
selectCol.setCellValueFactory(
new PropertyValueFactory<>("selected"));
nomeCol.setCellValueFactory(
new PropertyValueFactory<>("nome"));
idadeCol.setCellValueFactory(
new PropertyValueFactory<>("idade"));
enderecoCol.setCellValueFactory(
new PropertyValueFactory<>("endereco"));

selectCol.setCellFactory(
CheckBoxTableCell.forTableColumn(selectCol));
nomeCol.setCellFactory(
TextFieldTableCell.forTableColumn());


tabela.setItems(listaDeClientes());
}

private ObservableList<Cliente> listaDeClientes() {
return FXCollections.observableArrayList(
new Cliente("Antonio", 28, "Rua Alvenaria 22"),
new Cliente("Bruno", 19, "Rua São Domingos 108"),
new Cliente("Manoel", 45, "Rua Valentim 05"),
new Cliente("Cassandra", 33, "Rua Palmeira 234"),
new Cliente("Roberto", 69, "Rua Jean Nassif 56"),
new Cliente("Mariana", 16, "Av Rendeiras 78")
);
}

// definição da classe Cliente
}

Podemos editar o nome na coluna ao clicar duas vezes em uma célula:


Criando colunas aninhadas para Endereço

Vamos supor que agora eu quero separar a informação na coluna “Endereço” em duas colunas, “Rua” e “Número”. Para deixar claro que essas duas informações compõem o endereço, eu posso criar essas duas colunas como colunas aninhadas da coluna endereço, ou seja, duas sub-colunas.

Precisamos fazer algumas alterações no código, como separar a informação de endereço em dois novos atributos da classe Cliente e adicionar as colunas aninhadas no fxml. A classe Cliente ficará:

public static class Cliente {
private final SimpleBooleanProperty selected;
private final SimpleStringProperty nome;
private final SimpleIntegerProperty idade;

// o atributo endereço foi substitído por rua e numero
private final SimpleStringProperty rua;
private final SimpleIntegerProperty numero;


public Cliente(String nome, Integer idade, String rua, Integer numero) {
this.selected = new SimpleBooleanProperty(false);
this.nome = new SimpleStringProperty(nome);
this.idade = new SimpleIntegerProperty(idade);
this.rua = new SimpleStringProperty(rua);
this.numero = new SimpleIntegerProperty(numero);

}

public boolean isSelected() {
return selected.get();
}

public SimpleBooleanProperty selectedProperty() {
return selected;
}

public void setSelected(boolean selected) {
this.selected.set(selected);
}

public String getNome() {
return nome.get();
}

public SimpleStringProperty nomeProperty() {
return nome;
}

public void setNome(String nome) {
this.nome.set(nome);
}

public int getIdade() {
return idade.get();
}

public SimpleIntegerProperty idadeProperty() {
return idade;
}

public void setIdade(int idade) {
this.idade.set(idade);
}

public String getRua() {
return rua.get();
}

public SimpleStringProperty ruaProperty() {
return rua;
}

public void setRua(String rua) {
this.rua.set(rua);
}

public int getNumero() {
return numero.get();
}

public SimpleIntegerProperty numeroProperty() {
return numero;
}

public void setNumero(int numero) {
this.numero.set(numero);
}

}

No FXML declaramos as duas novas colunas aninhadas na coluna “Endereço”:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="AppController">

<padding>
<Insets top="20" bottom="20" left="20" right="20"/>
</padding>

<TableView fx:id="tabela" editable="true">
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
<columns>
<TableColumn fx:id="selectCol"
text=""
minWidth="40"
maxWidth="40"/>
<TableColumn fx:id="nomeCol"
text="Nome"/>
<TableColumn fx:id="idadeCol"
text="Idade"
minWidth="70"
maxWidth="70"/>
<TableColumn fx:id="enderecoCol"
text="Endereço">
<columns>
<TableColumn fx:id="ruaCol"
text="Rua"/>
<TableColumn fx:id="numeroCol"
text="Número"
minWidth="70"
maxWidth="70"/>
</columns>
</TableColumn>

</columns>
</TableView>
</VBox>

O restante do controller ficará:

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;

import java.net.URL;
import java.util.ResourceBundle;

public class AppController implements Initializable {
@FXML
private TableView<Cliente> tabela;
@FXML
private TableColumn<Cliente, Boolean> selectCol;
@FXML
private TableColumn<Cliente, String> nomeCol;
@FXML
private TableColumn<Cliente, Integer> idadeCol;
@FXML
private TableColumn<Cliente, String> enderecoCol;
@FXML
private TableColumn<Cliente, String> ruaCol;
@FXML
private TableColumn<Cliente, Integer> numeroCol;


@Override
public void initialize(URL location, ResourceBundle resources) {
selectCol.setCellValueFactory(
new PropertyValueFactory<>("selected"));
nomeCol.setCellValueFactory(
new PropertyValueFactory<>("nome"));
idadeCol.setCellValueFactory(
new PropertyValueFactory<>("idade"));

ruaCol.setCellValueFactory(
new PropertyValueFactory<>("rua"));
numeroCol.setCellValueFactory(
new PropertyValueFactory<>("numero"));


selectCol.setCellFactory(
CheckBoxTableCell.forTableColumn(selectCol));
nomeCol.setCellFactory(
TextFieldTableCell.forTableColumn());

tabela.setItems(listaDeClientes());
}

private ObservableList<Cliente> listaDeClientes() {
return FXCollections.observableArrayList(
new Cliente("Antonio", 28, "Rua Alvenaria", 22),
new Cliente("Bruno", 19, "Rua São Domingos", 108),
new Cliente("Manoel", 45, "Rua Valentim", 05),
new Cliente("Cassandra", 33, "Rua Palmeira", 234),
new Cliente("Roberto", 69, "Rua Jean Nassif", 56),
new Cliente("Mariana", 16, "Av Rendeiras", 78)
);
}

//declaração da classe Cliente...
}

O resultado final fica da seguinte forma:


Conclusão

O TableView é provavelmente um dos componentes mais complexos do JavaFX para se trabalhar, devido à grande quantidade de classes auxiliares, uso intenso de Generics e à grande flexibilidade que esse componente proporciona. Nesse artigo vimos alguns casos não-triviais de utilização, da maneira mais simples e direta possível, incluindo a inserção de checkbox e campos de texto nas células da tabela e o uso de colunas aninhadas.