Swing y JFC (Java Foundation Classes)

Con la clase JTable, se pueden mostrar tablas de datos, y opcionalmente permitir que el usuario los edite. JTable no contiene ni almacena datos; simplemente es una vista de nuestros datos. Aqu� podemos ver una tabla t�pica mostrada en un ScrollPane.

El resto de esta p�gina explica como realizar algunas de las tareas m�s comunes relacionadas con las tablas. Aqu� est�n los t�picos cubiertos por est� p�gina.

.�Crear una Tabla Sencilla

Intenta esto:
  1. Compila y ejecuta la aplicaci�n. el fichero fuente es SimpleTableDemo.java.
  2. Pulsa sobre la celda que contiene "Snowboarding".

    Se selecciona toda la primera fila, indicando que has seleccionado los datos de Mary Campione. Una especial iluminaci�n indica que la celda "Snowboarding" es editable. Generalmente, se empieza a editar una celda de texto haciendo doble-click en ella.

  3. Posiciona el cursor sobre "First Name". Ahora pulsa el bot�n del rat�n y arr�strala hacia la derecha.

    Como puedes ver, los usuarios pueden modificar las columnas en las tablas.

  4. Posiciona el cursor justo a la derecha de una columna de cabecera. Ahora pulsa el bot�n del rat�n y arrastralo a derecha o izquierda.

    La columna cambia su tama�o, y las dem�s columnas se ajustan para rellenar el espacio sobrante.

  5. Redimensiona la ventana que contiene la tabla para que sea tan grande como para contener la tabla completa.

    Todas las celdas de la tabla se agrandan, expandi�ndose para llenar el espacio extra.

Aqu� est� el c�digo que implementa la tabla en SimpleTableDemo.java.

Object[][] data = {
    {"Mary", "Campione", 
     "Snowboarding", new Integer(5), new Boolean(false)},
    {"Alison", "Huml", 
     "Rowing", new Integer(3), new Boolean(true)},
    {"Kathy", "Walrath",
     "Chasing toddlers", new Integer(2), new Boolean(false)},
    {"Mark", "Andrews",
     "Speed reading", new Integer(20), new Boolean(true)},
    {"Angela", "Lih",
     "Teaching high school", new Integer(4), new Boolean(false)}
};

String[] columnNames = {"First Name", 
                        "Last Name",
                        "Sport",
                        "# of Years",
                        "Vegetarian"};

final JTable table = new JTable(data, columnNames);

El ejemplo SimpleTableDemo utiliza uno de los constructores de JTable que aceptan datos directamente.

  • JTable(Object[][] rowData, Object[] columnNames)
  • JTable(Vector rowData, Vector columnNames)

La ventaja de utilizar uno de estos constructores es que es sencillo. Sin embargo, estos constructores tambi�n tienen desventajas.

  • Autom�ticamente hacen todas las celdas editables.
  • Tratan igual a todos los tipos de datos. Por ejemplo, si una columna tiene datos Boolean, los datos pueden mostrarse como un CheckBox en la tabla. Sin embargo, si especificamos los datos como un argumento array o vector del constructor de JTable, nuestro dato Boolean se mostrar� como un string. Se pueden ver estas diferencias en las dos �ltimas columnas de los ejemplos ateriores.
  • Requieren que pongamos todos los datos de la tabla en un array o vector, lo que es inapropiado para algunos datos. Por ejemplo, si estamos ejemplarizando un conjunto de objetos desde una base de datos, podr�amos quere pedir los objetos directamente por sus valores, en vez de copiar todos los valores en un array o un vector.

Si queremos evitar estas restricciones, necesitamos implementar nuestro propio modelo de tabla como se describe en Crear un Modelo de Tabla.

.�A�adir una Tabla a un Contenedor

Es f�cil poner una tabla en un ScrollPane. S�lo necesitamos escribir una o dos l�neas de c�digo.

JScrollPane scrollPane = new JScrollPane(table);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));

El ScrollPane obtiene autom�ticamente las cabeceras de la tabla, que muestra los nombres de las columnas, y los pone en la parte superior de la tabla. Incluso cuando el usuario se desplaza hacia abajo, los nombres de columnas permanecen visibles en la parte superior del �rea de visi�n. El ScrollPane tambi�n intenta hacer que su �rea de visi�n sea del mismo tama�o que el tama�o preferido de la tabla. El c�digo anterior selecciona el tama�o preferido de la tabla con el m�todo setPreferredScrollableViewportSize.

Nota: Antes de Swing 1.0.2, el ScrollPane no pod�a obtener las cabeceras de la tabla a menos que se utilizara el m�todo JTable.createScrollPaneForTable para crearlo. Aqu� tenemos unos ejemplos de c�digo recomendado, antes y despu�s de Swing 1.0.2.
//1.0.1 code (causes deprecation warning in 1.0.2 and later releases).
scrollPane = JTable.createScrollPaneForTable(table);

//Recommended code (causes missing column names in 1.0.1).
scrollPane = new JScrollPane(table);

Si estamos utilizando una tabla sin un ScrollPane, debemos obtener los componentes de cabecera de la tabla y situarlos nosotros mismos. Por ejemplo.

container.setLayout(new BorderLayout());
container.add(table.getTableHeader(), BorderLayout.NORTH);
container.add(table, BorderLayout.CENTER);

.�Seleccionar y Cambiar la Anchura de las Columnas

Por defecto, todas las columnas de una tabla empiezan con la misma anchura, y las columnas rellenan autom�ticamente la anchura completa de la tabla. Cuando la tabla se ensancha o se estrecha (lo que podr�a suceder cuando el usuario redimensiona la ventana que la contiene) la anchura de todas las columnas cambia apropiadamente.

Cuando el usuario redimensiona una columna, arrastando su borde derecho, todas las dem�s deben cambiar su tama�o. Por defecto, el tama�o de la tabla permace igual, y todas las columnas a la derecha del punto de arrastre acomodan su tama�o al espacio a�adido o eliminado desde la columna situada a la izquierda del punto de arrastre.

Las siguientes figuras ilustran el comportamiento de redimensionado por defecto.

Inicialmente, las columnas tienen la misma anchura.

Cuando el usuario redimensiona una columna, alguna de las otras columnas debe ajustar su tama�o para que el tama�o de la tabla permanezca igual.

Cuando se redimensiona toda la tabla, todas las columnas se redimensionan.

Para personalizar la anchura inicial de las columnas, podemos llamar al m�todo setPreferredWidth con cada una de las columnas de la tabla. Este selecciona tanto las anchuras preferidas de las clumnas como sus anchuras relativas aproximadamente. Por ejemplo, si a�adimos el siguiente c�digo a SimpleTableDemo haremos que la tercera columna se mayor que las otras.

TableColumn column = null;
for (int i = 0; i < 5; i++) {
    column = table.getColumnModel().getColumn(i);
    if (i == 2) {
        column.setPreferredWidth(100); //sport column is bigger
    } else {
        column.setPreferredWidth(50);
    }
}
Nota: el m�todo setPreferredWidth fue primero introducido en Swing 1.1 beta 2. Para versiones anterior debemos utilizar setMinWidth, asegur�ndonos de llamarlo sobre cada columna, (de otro modo, las columnas que no lo hagamos ser�n muy finas).

Como muestra el c�digo anterior, cada columna de la tabla est� representada por un objeto TableColumn. Junto a setPreferredWidth, TableColumn tambi�n suministra m�todos para obtener la anchura m�nima, m�xima y actual de una columna. Para un ejemplo de selecci�n de anchura de celdas basada en la cantidad de espacio necesario para mostrar el contenido de las celdas, puedes ver el m�todo initColumnSizes en TableRenderDemo.java, que se explica en Mayor personalizaci�n del Visionado y del manejo de Eventos.

Cuando el usuario redimensiona expl�citamente las columnas, los nuevos tama�os no s�lo se convierten en la anchura actual de la columna, sino que tambi�n se convierten en la anchura preferida. Si embargo, cuando las columnas se redimensionan como resultado de un cambio de anchura de la tabla, las anchuras preferidas de las columnas no cambian.

Podemos cambiar el comportamiento de redimensionado de una tabla llamando al m�todo setAutoResizeMode. El argumento de este m�todo debe ser uno de estos valores (definidos como constantes en JTable).

AUTO_RESIZE_SUBSEQUENT_COLUMNS
Por defecto. Adem�s de redimensionar la columna a la izquierda del punto de arrastre, ajusta los tama�os de todas las columnas situadas a la derecha del punto de arrastre.
AUTO_RESIZE_NEXT_COLUMN
Ajusta s�lo las columnas inmediatas a la izquierda y derecha del punto de arrastre.
AUTO_RESIZE_OFF
Ajusta el tama�o de la tabla.
Nota: Antes de la versi�n Swing 1.1 Beta, el modo por defecto era AUTO_RESIZE_ALL_COLUMNS. Sin embargo, este modo no es intuitivo por eso se cambi� el modo por defecto a: AUTO_RESIZE_SUBSEQUENT_COLUMNS.

.�Detectar Selecciones de Usuario

El siguiente fragmento de c�digo muestra c�mo detectar cuando el usuario selecciona una fila de la tabla. Por defecto, una tabla permite que el usuario selecciona varias filas -- no columnas o celdas individuales -- y las filas seleccionadas deben ser contiguas. Utilizando el m�todo setSelectionMode, el siguiente c�digo especifica que s�lo se puede seleccionar una fila cada vez. Puedes encontrar el programa completo enSimpleTableSelectionDemo.java.

table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
...
ListSelectionModel rowSM = table.getSelectionModel();
rowSM.addListSelectionListener(new ListSelectionListener() {
    public void valueChanged(ListSelectionEvent e) {
        ListSelectionModel lsm = (ListSelectionModel)e.getSource();
        if (lsm.isSelectionEmpty()) {
            ./no rows are selected
        } else {
            int selectedRow = lsm.getMinSelectionIndex();
            ./selectedRow is selected
        }
    }
});

SimpleTableSelectionDemo tambi�n tiene c�digo (no incluido en el fragmento anterior) que cambia la orientaci�n de la selecci�n de la tabla. Cambiando un par de valores booleanos, podemos permitir que la tabla acepte selecciones de columnas o de celdas individuales en vez de seleccionar filas.

Para m�s informaci�n y ejemplos de implementaci�n de selecciones, puedes ver Escribir un Oyente de List Selection.

.�Crear un Modelo de tabla

Como se ha visto, toda tabla obtiene sus datos desde un objeto que implemente el interface TableModel.

El constructor de JTable usado por SimpleTableDemo crea su modelo de tabla con este c�digo.

new AbstractTableModel() {
    public String getColumnName(int col) { 
        return columnNames[col].toString(); 
    }
    public int getRowCount() { return rowData.length; }
    public int getColumnCount() { return columnNames.length; }
    public Object getValueAt(int row, int col) { 
        return rowData[row][col]; 
    }
    public boolean isCellEditable(int row, int col) { return true; }
    public void setValueAt(Object value, int row, int col) {
        rowData[row][col] = value;
	fireTableCellUpdated(row, col);
    }
}

C�mo muestra el c�digo anterior, implementar un modelo de tabla puede ser sencillo. Generalmente, se implementa en una subclase de la clase AbstractTableModel.

Nuestro modelo podr�a contener sus datos en un array, un vector o un hashtable, o podr�a obtener los datos desde una fuente externa como una base de datos. Incluso podr�a generar los datos en tiempo de ejecuci�n.

Aqu� est� de nuevo una imagen de una tabla implementada por TableDemo, que tiene un modelo de tabla personalizado.

Esta tabla es diferente de la de SimpleTableDemo en estas cosas.

  • El modelo de tabla de SimpleTableDemo, habiendo sido creado autom�ticamente por JTable, no es suficentemente inteligente para saber que la columna '# of Years' contiene n�meros (que generalmente deben alinearse a la derecha). Tampoco sabe que la columna 'Vegetarian' contiene un valor booleano, que pueden ser represantados por checkboxes. El modelo de datos personalizado de TableDemo, aunque sencillo, puede determinar los tipos de datos, ayudando a JTable a mostrar los datos en el mejor formato.
  • En SimpleTableDemo, todas las celdas son editables. En TableDemo, hemos implementado el modelo de tabla personalizado, para que no permita editar la columna 'name' pero, sin embargo, si se pueden editar las otras columnas.

Aqu� est� el c�digo de TableDemo.java que es diferente del c�digo de SimpleTableDemo.java. Las partes en negrita indican el c�digo que hace que este modelo de tabla sea diferente del modelo de tabla definido autom�ticamente por SimpleTableDemo.

public TableDemo() {
    ...
    MyTableModel myModel = new MyTableModel();
    JTable table = new JTable(myModel);
    table.setPreferredScrollableViewportSize(new Dimension(500, 70));

    //Create the scroll pane and add the table to it.
    JScrollPane scrollPane = new JScrollPane(table);

    //Add the scroll pane to this window.
    setContentPane(scrollPane);
    ...
}

class MyTableModel extends AbstractTableModel {
    final String[] columnNames = ./same as before...
    final Object[][] data = ./same as before...

    public int getColumnCount() {
        return columnNames.length;
    }
    
    public int getRowCount() {
        return data.length;
    }

    public String getColumnName(int col) {
        return columnNames[col];
    }

    public Object getValueAt(int row, int col) {
        return data[row][col];
    }

    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
        if (col < 2) { 
            return false;
        } else {
            return true;
        }
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    public void setValueAt(Object value, int row, int col) {
        ./debugging code not shown...
        ./ugly class cast code for Integers not shown...
        data[row][col] = value;
        ./debugging code not shown...
    }
    ...

.�Detectar Cambios de Datos

Una tabla y su modelo detectan autom�ticamente si el usuario edita los datos de la tabla. Sin embargo, si los datos cambian por otra raz�n, debemos realizar unos pasos especiales para indicar a la tabla y a su modelo el cambio de los datos. Si no implementamos un modelo de tabla, como en SimpleTableDemo, tambi�n debemos realizar los pasos especiadl para ver cuando el usuario edita los datos de la tabla.

Para disparar un evento table-model, el modelo llama al m�todo fireTableRowsInserted, que est� definido por la clase AbstractTableModel. Otros m�todos fireXxxx que define la clase AbstractTableModel para ayudarnos a lanzar eventos table-model son fireTableCellUpdated, fireTableChanged, fireTableDataChanged, fireTableRowsDeleted, fireTableRowsInserted, fireTableRowsUpdated, y fireTableStructureChanged.

Si tenemos una clase como SimpleTableDemo que es una tabla o un modelo de tabla, pero necesita reaccionar a los cambios en un modelo de tabla, necesitamos hacer algo especial para detectar cuando el usuario edita los datos de la tabla. Espec�ficamente, necesitamos registrar un oyente de table-model. A�adiendo el c�digo en negrita del siguiente fragmento hace que SimpleTableDemo reaccione ante los cambios de datos.

public class SimpleTableDemo ... implements TableModelListener {
    ...
    public SimpleTableDemo() {
        ...
	model = table.getModel();
        model.addTableModelListener(this);
        ...
    }

    public void tableChanged(TableModelEvent e) {
        ...
        int row = e.getFirstRow();
        int column = e.getColumn();
        String columnName = model.getColumnName(column);
        Object data = model.getValueAt(row, column);

        ./ Do something with the data...
    }
    ...
}

.�Conceptos: Editores de Celdas e Int�rpretes

Antes de seguir adelante, necesitamos comprender como las tablas dibujan sus celdas. Podr�amos esperar que cada celda de la tabla fuera un componente. Sin embargo, por razones de rendimiento, las tablas Swing no est�n implementadas de esta forma.

En su lugar, se utiliza un sencillo int�rprete de celdas para dibujar todas las celdas de una columna. Frecuentemente este int�rprete es compartido entre todas las columnas que contienen el mismo tipo de datos. Podemos pensar en el int�rprete como un sello de caucho que las tablas utilizan para estampar los datos formateados apropiadamente en cada celda. Cuando el usuario empieza a editar los datos de una celta, un editor de celdas toma el control sobre la edici�n de la celda.

Por ejemplo, todas las celdas de la columna '# of Years' de TableDemo contienen datos num�ricos -- espec�ficamente un objeto Integer. Por defecto, el int�rprete para una columna num�rica utiliza un sencillo ejemplar de JLabel para dibujar los n�meros apropiados, alineados a la derecha, en las celdas de la columna. Si el usuario empieza a editar una de las celdas, el editor por defecto utiliza un JTextField alineado a la derecha para controlar la edici�n.

Para elegir el int�rprete que muestra la celdas de una columna, una tabla primero determina si hemos especificado un �nterprete para esa columna particular. Si no lo hacemos, la tabla llama al m�todo getColumnClass del modelo de tabla, que obtiene el tipo de datos de las celdas de la columna. Luego, la tabla compara el tipo de datos de la columna con una lista de tipos de datos para los que hay registrados unos int�rpretes. Esta lista es inicializada por la tabla, pero podemos a�adirle elementos o cambiarla. Actualmente, las tablas ponen los siguientes tipos en la lista.

  • Boolean -- interpretado con un checkbox.
  • Number -- interpretado con una etiqueta alineada a la derecha.
  • ImageIcon -- interpretado por una etiqueta centrada.
  • Object -- interpretado por una etiqueta que muestra el valor String del objeto.

Las tablas eligen sus editores de celdas utilizando un algoritmo similar.

Recuerda que si dejamos que una tabla cree su propio modelo, utiliza Object como el tipo de cada columna. TableDemo.java muestra como especificar los tipos de datos de las columnas con m�s precisi�n.

Las siguientes secciones explican c�mo pesonalizar el visionado y edici�n de celdas especificando int�rpretes y editores de celdas para columnas o tipos de datos.

.�Validar el Texto Introducido por el Usuario

En los ejemplos de tablas que hemos visto hasta ahora, el usuario pod�a introducir cualquier texto en la columna '# of Years'. SimpleTableDemo no comprueba el valor de los datos. El ejemplo TableDemo est� ligeramente mejorado en que cuando el usuario hace la edici�n, el c�digo comprueba si la entrada puede ser pasada a un entero. Sin embargo, TableDemo debe usar un c�digo m�s ambicioso para convertir el string devuelto por el editor de celdas por defecto en un Integer. Si no hace la conversi�n, el tipo real de los datos podr�a cambiar de Integer a String.

Lo que realmente queremos hacer es comprobar la entrada del usuario mientras la est� tecleando, y hacer que el editor de celdas devuelva un Integer en lugar de un string. Podemos conseguir una o estas dos tareas utilizando un campo de texto personalizado para controlar la edici�n de la celda.

Un campo de texto personalizado, puede chequear la entrada del usuario mientras la est� tecleando o despu�s de que haya indicado el final (pulsado la tecla return, por ejemplo). Llamamos a estos tipos de validaci�n, chequeo de pulsaci�n y chequeo de acci�n, respectivamente.

El siguiente c�digo, tomado de TableEditDemo.java, configura un campo de texto con chequeo de pulsaci�n. Las l�neas en negrita hacen que el campo de texto sea el editor para todas las celdas que contengan datos del tipo Integer.

final WholeNumberField integerField = new WholeNumberField(0, 5);
integerField.setHorizontalAlignment(WholeNumberField.LEFT);

DefaultCellEditor integerEditor = 
    new DefaultCellEditor(integerField) {
        //Override DefaultCellEditor's getCellEditorValue method
        //to return an Integer, not a String.
        public Object getCellEditorValue() {
            return new Integer(integerField.getValue());
        }
    };
table.setDefaultEditor(Integer.class, integerEditor);

La clase WholeNumberField utilizada arriba es una subclase de JTextField personalizada que permite al usuario introducir s�lo dos d�gitos. El m�todo getValue devuelve el valor int del contenido de WholeNumberField Puedes ver C�mo usar TextField para m�s informaci�n sobre WholeNumberField. Esta p�gina tambi�n proporciona un textfield con validaci�n de prop�sito general, llamado DecimalField, que podemos personalizar para validar cualquier formato de n�mero que le especifiquemos.

.�Usar un ComboBox como un Editor

Aqu� hay un ejemplo de configuraci�n de un ComboBox como editor de celda. Las l�neas en negrita del c�digo configuran el ComboBox para editar una columna, en vez de para un tipo de dato espec�fico.

TableColumn sportColumn = table.getColumnModel().getColumn(2);
...
JComboBox comboBox = new JComboBox();
comboBox.addItem("Snowboarding");
comboBox.addItem("Rowing");
comboBox.addItem("Chasing toddlers");
comboBox.addItem("Speed reading");
comboBox.addItem("Teaching high school");
comboBox.addItem("None");
sportColumn.setCellEditor(new DefaultCellEditor(comboBox));

Aqu� hay una imagen el editor ComboBox en uso.

El editor ComboBox est� implementado en TableRenderDemo.java, que se explica en m�s detalle en Mayor Personalizaci�n de Visionado y de Manejo de Eventos.

.�Especificar otros Editores

Como se vi� en la secci�n anterior, podemos seleccionar un editor para una columna utilizando el m�todo setCellEditor de Tablecolum, o para un tipo de datos espec�fico usando el m�todo setDefaultEditor de JTable. Para ambos m�todos, podemos especificar un argumento que implemente el interface TableCellEditor. Afortunadamente, la clase DefaultCellEditor implementa este interface y proporciona constructores para permitir especificar y editar componentes que sean JTextField, JCheckBox, o JComboBox. Normalmente no tenemos que especificar expl�citamente un checkbox como un editor, ya que las columnas con datos Boolean autom�ticamente usan un editor y un int�rprete CheckBox.

�Y si queremos especificar un editor que no sea un textfield, checkbox, o combobox? Bien, como DefaultCellEditor no soporta otros tipos de componentes, debemos trabajar un poco m�s. Necesitamos crear una subclase del editor de componente deseado, y esta subclase debe implementar el interface TableCellEditor. Luego configuramos el componente como un editor para un tipo de dato o una columna, utilizando los m�todos setDefaultEditor o setCellEditor, respectivamente.

Aqu� hay una imagend e una tabla con un di�logo que sirve, indirectamente, como editor de celda. Cuando el usuario empieza a editar una celda en el columna, 'Favorite Color', un bot�n, (el verdadero editor de celda) aparece y trae el di�logo, con el que el usuario puede elegir un color diferente.

Podemos encontrar el c�digo fuente en TableDialogEditDemo.java. el ejemplo tambi�n necesita WholeNumberField.java.

.�Mayor Personalizaci�n de Visionado y de Manejo de Eventos

Ya hemos visto como especificar editores de celdas. Tambi�n podemos especificar int�rpretes para celdas y para cabeceras de columnas. Los int�rpretes personalizados permiten mostrar datos de formas personalizadas y especificar texto de ayuda (tooltips) para que lo muestre la tabla.

Aunque los int�rpretes determinan c�mo se muestran las celdas o cabeceras de columnas, no pueden manejar eventos. Para detectar los eventos que tienen lugar dentro de una tabla, deber�amos elegir la t�cnica apropiada para ordenar el evento en el que estamos interesados. Para una celda que esta siendo editada, el editor deber�a procesar eventos. Para detectar selecciones y deselecciones de fila/columna/celda, se utiliza un oyente de selection como se describe en Detectar Selecciones del Usuario. Para editar pulsaciones del rat�n en una cabecera de columna, podemos registrar un oyente de mouse en la cabecera de la tabla. (Puedes ver un ejemplo en TableSorter.java). Para detectar otros eventos, podemos registrar el oyente apropiado sobre el objeto JTable.

Crear un int�rprete personalizado puede ser tan sencillo como crear una subclse de un componente existente y luego implementar el �nico m�todo del interface TableCellRenderer. En la figura anterior, el int�rprete de color utilizado para la celda "Favorite Color" es una subclase de JLabel. Podemos encontrar el c�digo del int�rprete en la clase interna de TableDialogEditDemo.java. Aqu� est� el c�digo que registra el ejemplar de ColorRenderer como el int�rprete por defecto para todos los datos Color.

table.setDefaultRenderer(Color.class, new ColorRenderer(true));

Podemos especificar un int�rprete especifico de celda, si queremos. Para hacer esto, nocesitamos definir una subclase de JTable que sobreescriba el m�todo getCellRenderer. Por ejemplo, el siguiente c�digo hace que la primera celda de la primera columna de la tabla use un int�rprete personalizado.

TableCellRenderer weirdRenderer = new WeirdRenderer();
table = new JTable(...) {
    public TableCellRenderer getCellRenderer(int row, int column) {
	if ((row == 0) && (column == 0)) {
	    return weirdRenderer;
	}
	// else...
	return super.getCellRenderer(row, column);
    }
};

Para a�adir tool-tips a las celdas o columnas de cabecera, necesitamos obtener y crear el int�rprete de celda o cabecera y luego utilizar el m�todo setToolTipText del componente del int�rprete. TableRenderDemo.java a�ade tool-tips tanto a las celdas como a la cabecera de la columna 'Sport' con el siguiente c�digo.

//Set up tool tips for the sport cells.
DefaultTableCellRenderer renderer =
        new DefaultTableCellRenderer();
renderer.setToolTipText("Click for combo box");
sportColumn.setCellRenderer(renderer);

//Set up tool tip for the sport column header.
TableCellRenderer headerRenderer = sportColumn.getHeaderRenderer();
if (headerRenderer instanceof DefaultTableCellRenderer) {
    ((DefaultTableCellRenderer)headerRenderer).setToolTipText(
             "Click the sport to see a list of choices");
} 

Una interesante caracter�stica de TableRenderDemo es c�mo determina el tama�o de sus columnas. Por cada columna, TableRenderDemo obtiene los componentes utilizados para int�rpretar las celdas y la cabecera de columna. Luego pregunta a los componentes cu�nto espacio necesitan. Finalmente, utiliza esa informaci�n para seleccionar al anchura de la columna.

TableColumn column = null;
Component comp = null;
int headerWidth = 0;
int cellWidth = 0;
Object[] longValues = model.longValues;

for (int i = 0; i < 5; i++) {
    column = table.getColumnModel().getColumn(i);

    comp = column.getHeaderRenderer().
                     getTableCellRendererComponent(
                         null, column.getHeaderValue(), 
                         false, false, 0, 0);
    headerWidth = comp.getPreferredSize().width;

    comp = table.getDefaultRenderer(model.getColumnClass(i)).
                     getTableCellRendererComponent(
                         table, longValues[i],
                         false, false, 0, i);
    cellWidth = comp.getPreferredSize().width;
    ./debugging code not shown...
    column.setPreferredWidth(Math.max(headerWidth, cellWidth));
}

./In the model:
public final Object[] longValues = {"Angela", "Andrews", 
                                    "Teaching high school",
                                    new Integer(20), Boolean.TRUE};

.�Ordenaci�n y otras Manipulaciones de Datos

Una forma de realizar una manipulaci�n de datos como la ordenaci�n es utilizar uno o m�s modelos de tablas especializados (manipuladores de datos), adem�s del modelo que proporciona los datos (el modelo de datos). Los manipuladores de datos deber�an situarse entre la tabla y el modelo de datos, como muestra la siguiente figura.

Podemos utilizar las clases TableMap y TableSorter cuando implementemos nuestro manipulador de datos. TableMap implementa TableModel y sirve como una superclase para manipuladores de datos.

TableSorter es una subclase de TableMap que ordena los datos proporcionados por otro modelo de tabla. Tambi�n podemos cambiar estas clases, utiliz�ndolas como un base para escribir nuestros propios manipuladores, o utilizarlas tal como son para proporcionar la funcionalidad de ordenaci�n.

Para implementar la ordenaci�n con TableSort, necesitamos s�lo tres l�neas de c�digo. El siguiente listado muestra las diferencias entre TableDemo y su primo ordenado, TableSorterDemo.

TableSorter sorter = new TableSorter(myModel); //ADDED THIS
//JTable table = new JTable(myModel);          //OLD
JTable table = new JTable(sorter);             //NEW
sorter.addMouseListenerToHeaderInTable(table); //ADDED THIS

El m�todo addMouseListenerToHeaderInTable a�ade un oyente de mouse que detecta pulsaciones sobre las cabeceras de columna. Cuando el oyente detecta un click, ordena las filas b�sandose en la columna pulsada. Por ejemplo, cuando pulsamos sobre "Last Name", las filas son reordenadas para que la fila con "Andrews" se convierta la primera. Cuando volvemos a pulsar de nuevo la cabecera de columna, las filas se ordenan en orden inverso.

.�El API Table

Las tablas de esta secci�n s�lo cubren una parte de este API. Para m�s informaci�n puedes ver el API de JTable y para distintas clases y paquetes table package. El API para usar tablas se divide en estas categor�as.

.�Clases e Interfaces Relacionados con las Tablas

Clase/Interface Prop�sito
JTable El componente que presenta la tabla al usuario.
JTableHeader El componente que presenta los nombres de columnas al usuario. Por defecto, la tabla genera este componente autom�ticamente.
TableModel, AbstractTableModel Respectivamente, el interface que un modelo de tabla debe implementar y la superclase usual para implementaciones de modelos de tabla.
TableCellRenderer, DefaultTableCellRenderer Respectivamente, el interface que un int�rprete de celda debe implementar y la implementaci�n m�s usual.
TableCellEditor, DefaultCellEditor Respectivamente, el interface que debe implementar un editor de celda, y la implementaci�n m�s usual.
TableColumnModel, DefaultTableColumnModel Respectivamente, el interface que debe implementar un modelo de columna, y su implementaci�n usual. Normalmente no tenemos que tratar directamente con el modelo de columna a menos que necesitemos obtener el modelo de selecci�n de columna u obtener un �ndice de columna o un objeto.
TableColumn Controla todos los atributos de una columna de la tabla, incluyendo, redimensionado, anchuras m�nima, m�xima, preferida y actual; y editor/int�rprete opcional espec�fico de la columna.
DefaultTableModel Un modelo de tabla basado en Vector utilizado por JTable cuando construimos una tabla sin especificar modelo de datos ni datos.

.�Crear y Configurar una Tabla

M�todo/Constructor de JTable Prop�sito
JTable(TableModel)

JTable(TableModel, TableColumnModel)

JTable(TableModel, TableColumnModel, ListSelectionModel)

JTable()

JTable(int, int)

JTable(Object[][], Object[])

JTable(Vector, Vector)

Crea una tabla. El argumento opcional TableModel especifica el modelo que proporciona los datos de la tabla. Los argumentos opcionales TableColumnModel y ListSelectionModel permiten especificar el modelo de columna y el modelo de selecci�n. Como una alternativa para especificar un modelo de tabla, podemos suministrar datos y nombres de columnas utilizando un array o un Vector. Otra opci�n es no especificar datos, opcionalmente especificar el n�mero de filas y columnas (ambos enteros) que hayan en la tabla.
void setPreferredScrollableViewportSize(Dimension) Selecciona el tama�o de la parte visible de la tabla cuando se est� viendo dentro de un ScrollPane.
JTableHeader getTableHeader(Dimension) Obtiene el componente que muestra los nombres de columnas.

.�Manipular Columnas

M�todo Prop�sito
TableColumnModel getColumnModel()

(en JTable)

Obtiene el modelo de columna de la tabla.
TableColumn getColumn(int)

Enumeration getColumns()

(en TableColumnModel)

Obtiene uno o todos los objetos TableColumn de la tabla.
void setMinWidth(int)

void setPreferredWidth(int)

void setMaxWidth(int)

(en TableColumn)

Seleciona la anchura m�nima, preferida o m�xima de la columna.
int getMinWidth(int)

int getPreferredWidth()

int getMaxWidth()

int getWidth()

(en TableColumn)

Obtiene la anchura m�nima, preferida, m�xima o actual de la columna.

.�Usar Editores e Int�rpretes

M�todos Prop�sito
void setDefaultRenderer(Class, TableCellRenderer)

void setDefaultEditor(Class, TableCellEditor)

(en JTable)

Selecciona el int�rprete o editor usado, por defecto, para todas las celdas en todas las columnas que devuelvan el tipo de objetos especificado.
void setCellRenderer(TableCellRenderer)

void setCellEditor(TableCellEditor)

(en TableColumn)

Selecciona el int�rprete o editor usado para todas las celdas de esta columna.
TableCellRenderer getHeaderRenderer()

(en TableColumn)

Obtiene el int�rprete de cabecera para esta columna, que podemos personalizar.

.�Implementar Selecci�n

M�todo de JTable Prop�sito
void setSelectionMode(int) Selecciona los intervalos de selecci�n permitidos en la tabla. Los valores v�lidos est�n definidos en ListSelectionModel como SINGLE_SELECTION, SINGLE_INTERVAL_SELECTION, y MULTIPLE_INTERVAL_SELECTION (por defecto).
void setSelectionModel(ListSelectionModel)

ListSelectionModel getSelectionModel()

Selecciona u obtiene el modelo usado para controlar las selecciones de filas.
void setRowSelectionAllowed(boolean)

void setColumnSelectionAllowed(boolean)

void setCellSelectionEnabled(boolean)

Selecciona la orientaci�n de selecci�n de la tabla. El argumento booleano especifica si est� permitido el tipo de selecci�n particular. Por defecto, las selecci�n de filas est� permitida, y la de columna y celda no.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR