El JTable de Java es un gran componente para mostrar datos en una tabla de una forma rápida y sencilla, sin embargo en varias oportunidades se queda corta antes nuestras necesidades por lo que es necesario implementar nuestras propias tablas de datos, para ser capaces de mostrar por ejemplo, datos que a su vez contengan más datos, es decir, tablas compuestas.
Utilizaremos el IDE Netbeans y Java 8.
En este post implementaremos paso a paso el código necesario para crear una tabla que permita entre sus celdas el uso de sub tablas, para ser más precisos, una tabla que permita mostrar una lista de autores quienes tienen en su haber uno o varios libros como se ve en la imagen siguiente:
Paso 1. El proyecto
Creamos un proyecto Java con el nombre de «Sub Tablas Java» y la siguiente estructura:
Paso 2. El modelo
El modelo esta representados por dos clases, la clase Autor y la clase Libro las cuales están relacionadas por el tipo de relación «uno a muchos», es decir, un Autor puede escribir muchos libros.
A continuación el código de las clases, no se olviden de colocar los métodos GET y SET
Clase Autor:
package org.example.modelo; import java.util.ArrayList; /** * @see https://www.jc-mouse.net/ * @author mouse */ public class Autor { private String nombre; private String pais; private ArrayList<Libro> libros; public Autor() { } public Autor(String nombre, String pais, ArrayList<Libro> libros) { this.nombre = nombre; this.pais = pais; this.libros = libros; } //GET... //SET... }
Clase Libro:
package org.example.modelo; /** * @see https://www.jc-mouse.net/ * @author mouse */ public class Libro { private String titulo; private String genero; public Libro() { } public Libro(String titulo, String genero) { this.titulo = titulo; this.genero = genero; } //GET... //SET... }
Paso 3. El Modelo de la Tabla
Debemos implementar nuestro propio modelo para la tabla para poder interactuar con nuestros datos los cuales son para este ejemplo objetos de tipo Autor que a su vez contienen otros objetos, para realizar esto usaremos la clase AbstractTableModel.
package org.example.componentes; import java.util.ArrayList; import javax.swing.table.AbstractTableModel; import org.example.modelo.Autor; /** * @see https://www.jc-mouse.net/ * @author mouse */ public class TablaModelo extends AbstractTableModel { final String[] columnNames = {"Autor", "Pais de Origen", "Libro,Genero"}; private ArrayList<Autor> list = new ArrayList<>(); public void addAutor(Autor autor) { list.add(autor); //Notifica a los listeners que se ha insertado un nuevo dato //en el rango [firstRow, lastRow] fireTableRowsInserted(list.size() - 1, list.size() - 1); } @Override public int getRowCount() { return list.size(); } @Override public int getColumnCount() { return columnNames.length; } @Override public Object getValueAt(int rowIndex, int columnIndex) { Autor autor = list.get(rowIndex); switch (columnIndex) { case 0: return autor.getNombre(); case 1: return autor.getPais(); case 2: return autor.getLibros(); default: return null; } } @Override public String getColumnName(int col) { return columnNames[col]; } @Override public boolean isCellEditable(int row, int col) { return false; } }
También declaramos los nombres de las columnas, y aquí un detalle importante, nuestras sub tablas se mostraran dentro de una celda en una sola columna por lo que debemos también personalizar los headers de la tabla, pero esto lo veremos más adelante, por el momento solo declaramos los nombres de las columnas de la sub tabla en un String separada por comas, es decir de la forma «Libro,Genero».
Paso 4. El renderizador de celdas
Dado que los datos que se mostraran en la tabla no son los que usualmente se utilizan, debemos crear nuestro propio renderizador de celdas para personalizar la salida de datos por pantalla, sino hacemos esto, podemos obtener lo siguiente:
Implementamos la interface TableCellRenderer en nuestra clase CeldaRenderer de la siguiente forma:
package org.example.componentes; import java.awt.Component; import java.util.ArrayList; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellRenderer; import org.example.modelo.Libro; /** * @see https://www.jc-mouse.net/ * @author mouse */ public class CeldaRenderer implements TableCellRenderer { private int alturaMinima = -1; private int alturaActual = -1; @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { //si el valor recibido es un array if (value instanceof ArrayList) { ArrayList<Libro> libros = (ArrayList<Libro>) value; //de acuerdo al tamaño de la lista de libros //se ajusta la altura de la subtabla if (alturaMinima == -1) { alturaMinima = table.getRowHeight(); } if (!libros.isEmpty()) { if (alturaActual != libros.size() * alturaMinima) { alturaActual = libros.size() * alturaMinima; table.setRowHeight(row, alturaActual); } } //fin ajustes de altura de subtabla //El objeto devuelto sera un JTable return new JTable(new AbstractTableModel() { //sabemos que el numero de columnas de nuestra //sub tabla es de 2 final int cols = 2; @Override public int getColumnCount() { return cols; } @Override public int getRowCount() { return libros.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { Libro libro = libros.get(rowIndex); switch (columnIndex) { case 0: return libro.getTitulo(); case 1: return libro.getGenero(); default: return null; } } @Override public boolean isCellEditable(int row, int col) { return false; } }); } else {//no hacemos nada return table.getDefaultRenderer(value.getClass()) .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } } }
El código anterior lo que hace es, si el valor obtenido para la celda es un array, formateara la salida de esta en forma de un JTable, caso contrario retornara el valor por defecto.
Paso 5. El renderizador del encabezado
Como ya mencionamos anteriormente, como nuestros datos no son los usuales para un JTable, debemos personalizar la salida en pantalla de estos, ya lo hicimos para los datos del JTable, y ahora debemos hacer lo propio para los nombres de las columnas o el header de nuestra tabla.
Los nombres de nuestras columnas están dados por cadenas de texto, pero para el caso de nuestra sub tabla, usamos el formato «Libro,Genero», por lo que nuestro render lo que hará, es separar estos en un array usando el método split(«,») para luego mostrar cada elemento del array resultante en JLabels independientes y formateados. El código para la clase EncabezadoRenderer es el siguiente:
package org.example.componentes; import java.awt.Component; import java.awt.GridLayout; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.SwingConstants; import javax.swing.border.BevelBorder; import javax.swing.border.SoftBevelBorder; import javax.swing.table.TableCellRenderer; /** * @see https://www.jc-mouse.net/ * @author mouse */ public class EncabezadoRenderer extends JPanel implements TableCellRenderer { public EncabezadoRenderer() { setLayout(new GridLayout()); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { String[] str = value.toString().split(","); removeAll(); for (String s : str) { JLabel label = new JLabel(); label.setBorder(new SoftBevelBorder(BevelBorder.RAISED)); label.setHorizontalAlignment(SwingConstants.CENTER); label.setText(s); add(label); } return this; } }
Paso 6. Implementación del JTable
Finalmente debemos implementar los renders y el modelo a nuestra tabla, para esto, en nuestro diseñador de interfaz, en el frame SubTablaFrame, agregamos un JTable de la siguiente forma:
Desde el editor de código, declaramos el método iniciarTabla() y llamamos desde el constructor.
public class SubTablaFrame extends javax.swing.JFrame { /** * Creates new form SubTablaFrame */ public SubTablaFrame() { initComponents(); // iniciarTabla(); } private void iniciarTabla() { //Los datos TablaModelo model = new TablaModelo(); //1 libro ArrayList<Libro> libros1 = new ArrayList<>(); libros1.add(new Libro("Cuento de Navidad", "Novela")); Autor autor1 = new Autor("Charles Dickens", "UK", libros1); //2 libros ArrayList<Libro> libros2 = new ArrayList<>(); libros2.add(new Libro("El hobbit", "Novela")); libros2.add(new Libro("El Silmarillion", "Novela")); Autor autor2 = new Autor("J. R. R. Tolkien", "Sud Africa", libros2); //3 libros ArrayList<Libro> libros3 = new ArrayList<>(); libros3.add(new Libro("La isla misteriosa", "Novela")); libros3.add(new Libro("La jangada", "Novela")); libros3.add(new Libro("Doctor ox", "Cuentos")); Autor autor3 = new Autor("Julio Verne", "Francia", libros3); //cero libros Autor autor4 = new Autor("H. G. Wells", "UK", new ArrayList<>()); //se agregan al modelo model.addAutor(autor1); model.addAutor(autor2); model.addAutor(autor3); model.addAutor(autor4); //se establece el modelo de datos jTable1.setModel(model); //se asignan los render a las celdas y el encabezado jTable1.getTableHeader().setDefaultRenderer(new EncabezadoRenderer()); TableColumnModel tcm = jTable1.getColumnModel(); for (int it = 0; it < tcm.getColumnCount(); it++) { tcm.getColumn(it).setCellRenderer(new CeldaRenderer()); } }
Nuestra tabla con subtablas ya esta lista
enjoy!
Esta aplicacion permite escalar una imagen desde java sin perder las proporciones de la misma, utiliza SCALE_AREA_AVERAG[...]
Un SGA «Sistema de Gestión de Almacenes» es un programa informático destinado a gestionar las entradas y salidas de pro[...]
Necesitamos: Android Studio 2 tipos de fuente TTF Agregar Archivo de fuente al proyecto Paso 1: Crear carpeta assets Cli[...]
La Inteligencia Artificial (AI – Artificial Intelligence) estará presente en casi cada aspecto de nuestra vida en[...]
Matando dos pájaros de un solo tiro :), doy respuesta a un par de preguntas que están relacionadas, en este post veremos[...]
En un post anterior vimos como utilizar GSON para serializar un objeto java en JSON, en esta oportunidad se vera el proc[...]