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

Introducción a Spark Framework

Introducción a Spark Framework

Spark Framework es un conjunto de librerías para los lenguajes Java y Kotlin que nos sirve para el desarrollo rápido y s...

Botones circulares de Google Plus en java

Botones circulares de Google Plus en java

Continuación del tutorial «Google Circles en java» o.O 🙂 La Interfaz El proyecto consta de una sola interfaz la cual es...

Descargar Doodle Google Pacman

Descargar Doodle Google Pacman

Hace un par de años atras google para el aniversario del juego de Pacman saco un doodle en su homenaje, ese doodle que s...

Crear ayuda HTML para programa con JavaFX Swing

Crear ayuda HTML para programa con JavaFX Swing

En este post veremos como crear una aplicación de ayuda al usuario similar al extinto javahelp. Pero en esta oportunidad...

Reconocimiento Óptico de Caracteres con Tess4J

Reconocimiento Óptico de Caracteres con Tess4J

El reconocimiento óptico de caracteres o OCR (Optical Character Recognition), es un proceso dirigido a la digitalización...

Componente swing jcMousePanel v1.6.9

Componente swing jcMousePanel v1.6.9

Agregar una imagen a un jpanel o a un jframe no es complicado pero para un programador novato puede convertirse en todo...

Comparte lo que sabes

Categorias

Últimas entradas

En muchas ocasiones es necesaria la personalización de componentes java para que estos se adecuen a nuestros requerimien...

En este post mostramos como personalizar el Header (encabezado) de un componente JTable en Java colocando iconos, centra...

El JTable de Java es un gran componente para mostrar datos en una tabla de una forma rápida y sencilla, sin embargo en v...

En este post veremos un ejemplo sencillo de como descargar desde Internet archivos de cualquier tipo (*.jpg, *.png, *.gi...

Herramientas

Generador de Enlaces a Whatsapp