Sigueme en Facebook Sigueme en Twitter Sigueme en Instagram Sigueme en Youtube
JC Mouse Bolivia
Index / Java / Sub Tablas: Agregar tablas dentro de otras tablas en Java

Sub Tablas: Agregar tablas dentro de otras tablas en Java

Autor jc mouse miércoles, enero 29, 2020

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:

custom jtable

Paso 1. El proyecto

Creamos un proyecto Java con el nombre de «Sub Tablas Java» y la siguiente estructura:

proyecto java

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:

personalizar celdas de tabla

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:

user gui table

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

custom jtable

enjoy!

Tags

Si te ha gustado podrías compartirlo o dejar un comentario. ¡Muchas gracias!
Autor: JC Mouse

Yo soy yo :) JC Mouse, Soy orgullosamente boliviano soy fundador y CEO de la web jc-Mouse.net uno de las pocas web en emprendimiento y tecnología en Bolivia.

Toda la información que encuentres en este sitio es y sera completamente gratis siempre, puedes copiar, descargar y re-publicar si así lo deseas en otros blogs o sitios web, solo te pido a cambio que dejes una referencia a esta web. Esto nos ayuda a crecer y seguir aportando. Bye

Enjoy! :)

También Te Podría Interesar

Tablas virtuales en java (View)

Tablas virtuales en java (View)

Una Vista (View) es una Tabla Virtual cuyo contenido está definido por una consulta (SELECT), al igual que una tabla rea...

Generador de números aleatorios UNIX

Generador de números aleatorios UNIX

El Método Congruencial Lineal Mixto es el más utilizado en simulación en computadoras digitales y esta basado en una rel...

Introducción a Retrofit: Cliente para consumir un API REST

Introducción a Retrofit: Cliente para consumir un API REST

La página oficial de Retrofit, se describe así misma como «Un cliente REST seguro para Android y Java».  Y es así, ya qu...

Cliente Retrofit – Web Service

Cliente Retrofit – Web Service

Continuando el post de «Introducción a Retrofit» donde realizamos una breve preparación a lo que es el uso de la librerí...

Problema Resuelto: Personalizar JTable

Problema Resuelto: Personalizar JTable

PROBLEMA: Se tiene un JTable con 19 columnas y 50 registros, se desea personalizar el JTable de la siguiente manera:...

Métodos GET y POST en RestFul y JSON

Métodos GET y POST en RestFul y JSON

En este post veremos como enviar solicitudes GET y POST a un API RestFul  desde un dispositivo con android. Nuestra apli...

Comparte lo que sabes

Categorias

Últimas entradas

Si trabajas con redes sociales (RRSS) a continuación te muestro tres herramintas gratuitas que te ayudaran a la hora de...

Por lo general se usan transacciones a nivel base de datos y posteriormente se llaman estos a través de procedimientos a...

En este post, aprenderemos como conectar Visual Basic 6 con SQL Server, abrir una tabla, leer su contenido y mostrar est...

Lo que veremos en este post es la configuración del driver para PHP de SQL Server que ha creado Microsoft el cual permit...

Herramientas

Generador de Enlaces a Whatsapp