Las tablas representan uno de los elementos gráficos más completos dentro de una interfaz gráfica ya que son capaces de manejar datos con las principales acciones de una base de datos CRUD. Al mismo tiempo estos datos pueden ser representados de muy diferentes formas gracias a los modelos de pintado que se verán más adelante.
Lo primero que hay que tener en cuenta y es una de las principales diferencias con las tablas de Swing es que en javafx una tabla está compuesta por los siguientes elementos:
- TableView
- TableColumn
Por lo tanto una tabla está formada por columnas, donde cada una de ellas tendrá un id asignado que se utilizará para asociar una propiedad del objeto de se quiera representar en la tabla.
Al igual que pasa con los elementos que muestran datos como las listas o los spinner, la tabla se nutre de datos los cuales están dentro de una colección de tipo FXCollection.ObservableList. Por lo tanto lo primero que se debe hacer es crear una clase con el objeto que se quiere representar en la tabla el cual está compuesto por cada uno de los atributos que se quiera pero la diferencia que serán del tipo SimpleTipoProperty, lo que facilitará la asociación del elemento concreto con la columna que se quiere asociar
package fundamentos.utils; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; public class AlumnoTabla { // se definen cada una de las propiedades que más tarde se asociarán a cada una de las columnas de la tabla SimpleStringProperty nombre, apellido, email; SimpleIntegerProperty telefono; SimpleBooleanProperty matriculado; public AlumnoTabla(String nombre, String apellido, String email, int telefono, Boolean matriculado) { this.nombre = new SimpleStringProperty(nombre); this.apellido = new SimpleStringProperty(apellido); this.email = new SimpleStringProperty(email); this.telefono = new SimpleIntegerProperty(telefono); this.matriculado = new SimpleBooleanProperty(matriculado); } public String getNombre() { return nombre.get(); } public String getApellido() { return apellido.get(); } public String getEmail() { return email.get(); } public int getTelefono() { return telefono.get(); } public boolean isMatriculado() { return matriculado.get(); } }
Una vez se tiene el objeto «modelo», se contruye la tabla donde a cada columna se le asocia un atributo del modelo mediante el método setCellValueFactory() al cual se le pasa un objeto de tipo PropertyValueFactory<TipoObjeto,TipoDato>() al cual se le pasa el nombre de la propiedad que se quiere asociar. En el ejemplo anterior existía una propiedad llamada matriculado, para poder asignarla a la columna correspondiente sería:
colMatriculado.setCellValueFactory(new PropertyValueFactory<AlumnoTabla,Boolean>("matriculado"));
Por último se crea la colección de datos y se asocia a la tabla
ObservableList listaAlumnos = FXCollections.observableArrayList(); listaAlumnos.addAll(new AlumnoTabla("Nombre1", "Apellido1", "email1@gmail.com", 123, true), new AlumnoTabla("Nombre2", "Apellido2", "emai21@gmail.com", 123, false), new AlumnoTabla("Nombre3", "Apellido3", "emai31@gmail.com", 123, true), new AlumnoTabla("Nombre4", "Apellido4", "emai41@gmail.com", 123, false), new AlumnoTabla("Nombre5", "Apellido5", "emai51@gmail.com", 123, false)); // se asocia la lista de datos a la tabla tabla.setItems(listaAlumnos);
De esta forma la tabla directamente representa todos los datos de forma automática.
Para poder trabajar las opciones básicas de la tabla como añadir o borrar datos, se realizan las operaciones de forma directa sobre la lista asociada a la tabla junto con el método refresh() para actualizar todos los posibles cambios. Si se quiere realiza esta acción al pulsar un botón el código sería de la siguiente forma
// añadir un elemento a la tabla anadirTabla.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { listaAlumnos.add(new AlumnoTabla("NombreNuevo", "ApellidoNuevo", "nuevo@gmail.com", 123,true)); tabla.refresh(); } }); // eliminar un elemento de la tabla borrarTabla.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { listaAlumnos.remove(tabla.getSelectionModel().getSelectedIndex()); tabla.refresh(); } });
Para trabajar con la selección de la tabla se debe realizar todo desde el modelo de selección accediendo a el de la siguiente forma
tabla.getSelectionModel();
Así se está asegurado que se trabaja con los datos asociados y no con la parte gráfica. El método para poder acceder al objeto de la fila seleccionado se utiliza el método getSelectedItem()
tabla.getSelectionModel().getSelectedItem();
Si se quiere evaluar el cambio de la selección de la tabla se realiza mediante un escuchador
// evaluar la selección de la tabla según el cambio tabla.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { AlumnoTabla seleccionado = (AlumnoTabla) newValue; System.out.println(seleccionado.getNombre()); System.out.println(((AlumnoTabla)tabla.getSelectionModel().getSelectedItem()).getNombre()); } });
Los códigos utilizados en son:
package fundamentos.utils; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; public class AlumnoTabla { // se definen cada una de las propiedades que más tarde se asociarán a cada una de las columnas de la tabla SimpleStringProperty nombre, apellido, email; SimpleIntegerProperty telefono; SimpleBooleanProperty matriculado; public AlumnoTabla(String nombre, String apellido, String email, int telefono, Boolean matriculado) { this.nombre = new SimpleStringProperty(nombre); this.apellido = new SimpleStringProperty(apellido); this.email = new SimpleStringProperty(email); this.telefono = new SimpleIntegerProperty(telefono); this.matriculado = new SimpleBooleanProperty(matriculado); } public String getNombre() { return nombre.get(); } public String getApellido() { return apellido.get(); } public String getEmail() { return email.get(); } public int getTelefono() { return telefono.get(); } public boolean isMatriculado() { return matriculado.get(); } }
package fundamentos; import fundamentos.utils.AlumnoTabla; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.control.cell.PropertyValueFactory; import java.net.URL; import java.util.ResourceBundle; public class Controller_tabla implements Initializable { @FXML TableView tabla; @FXML TableColumn colNombre; @FXML TableColumn colApellido; @FXML TableColumn colMail; @FXML TableColumn colTelefono; @FXML TableColumn colMatriculado; @FXML Button anadirTabla; @FXML Button borrarTabla; ObservableList listaAlumnos = FXCollections.observableArrayList(); @Override public void initialize(URL location, ResourceBundle resources) { rellenarTabla(); modificarDatos(); } private void rellenarTabla() { listaAlumnos.addAll(new AlumnoTabla("Nombre1", "Apellido1", "email1@gmail.com", 123, true), new AlumnoTabla("Nombre2", "Apellido2", "emai21@gmail.com", 123, false), new AlumnoTabla("Nombre3", "Apellido3", "emai31@gmail.com", 123, true), new AlumnoTabla("Nombre4", "Apellido4", "emai41@gmail.com", 123, false), new AlumnoTabla("Nombre5", "Apellido5", "emai51@gmail.com", 123, false)); // se asocia a cada columna la propierdad que se desea del objeto indicado en el PropertyValueFactory colNombre.setCellValueFactory(new PropertyValueFactory<AlumnoTabla, String>("nombre")); colApellido.setCellValueFactory(new PropertyValueFactory<AlumnoTabla, String>("apellido")); colMail.setCellValueFactory(new PropertyValueFactory<AlumnoTabla, String>("email")); colTelefono.setCellValueFactory(new PropertyValueFactory<AlumnoTabla, Integer>("telefono")); colMatriculado.setCellValueFactory(new PropertyValueFactory<AlumnoTabla,Boolean>("matriculado")); // se asocia la lista de datos a la tabla tabla.setItems(listaAlumnos); // modifica el aspecto minimo y máximo que tendrá la tabla tabla.setPrefWidth(600); tabla.setMinWidth(0); // modifica el aspecto que tendrán las columnas colNombre.prefWidthProperty().bind(tabla.widthProperty().multiply(0.25)); colApellido.prefWidthProperty().bind(tabla.widthProperty().multiply(0.25)); colTelefono.prefWidthProperty().bind(tabla.widthProperty().multiply(0.25)); colMail.prefWidthProperty().bind(tabla.widthProperty().multiply(0.25)); // añade el boton para selección de columnas tabla.setTableMenuButtonVisible(true); // muestra un elemento si la tabla está vacía tabla.setPlaceholder(new Label("La tabla con contiene datos")); } private void modificarDatos() { // añadir un elemento a la tabla anadirTabla.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { listaAlumnos.add(new AlumnoTabla("NombreNuevo", "ApellidoNuevo", "nuevo@gmail.com", 123,true)); tabla.refresh(); } }); // eliminar un elemento de la tabla borrarTabla.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { listaAlumnos.remove(tabla.getSelectionModel().getSelectedIndex()); tabla.refresh(); } }); // evaluar la selección de la tabla según el cambio tabla.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { AlumnoTabla seleccionado = (AlumnoTabla) newValue; System.out.println(seleccionado.getNombre()); System.out.println(((AlumnoTabla)tabla.getSelectionModel().getSelectedItem()).getNombre()); } }); } }
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import java.lang.*?> <?import javafx.scene.layout.*?> <BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fundamentos.Controller_tabla"> <center> <TableView fx:id="tabla" editable="true" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="300.0" prefWidth="600.0" tableMenuButtonVisible="true" BorderPane.alignment="TOP_CENTER"> <columns> <TableColumn fx:id="colNombre" prefWidth="75.0" text="Nombre" /> <TableColumn fx:id="colApellido" prefWidth="75.0" text="Apellido" /> <TableColumn fx:id="colMail" prefWidth="75.0" text="Email" /> <TableColumn fx:id="colTelefono" prefWidth="94.0" text="Teléfono" /> <TableColumn fx:id="colMatriculado" prefWidth="75.0" text="Matrícula" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> </columnResizePolicy> </TableView> </center> <bottom> <GridPane prefHeight="100.0" prefWidth="608.0" BorderPane.alignment="CENTER"> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> </columnConstraints> <rowConstraints> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> </rowConstraints> <children> <Button fx:id="anadirTabla" mnemonicParsing="false" text="Añadir registro" GridPane.halignment="CENTER" /> <Button fx:id="borrarTabla" mnemonicParsing="false" text="Borrar seleccionado" GridPane.columnIndex="1" GridPane.halignment="CENTER" /> </children> </GridPane> </bottom> </BorderPane>