Académique Documents
Professionnel Documents
Culture Documents
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:
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.
La ventaja de utilizar uno de estos constructores es que es sencillo. Sin embargo, estos
constructores también tienen desventajas:
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);
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.
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.
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.
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
en SimpleTableSelectionDemo.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.
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:
public TableDemo() {
...
MyTableModel myModel = new MyTableModel();
JTable table = new JTable(myModel);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
/*
* 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...
}
...
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.
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 JTextFieldalineado 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:
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.
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.
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.
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:
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.
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:
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));
}
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.
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:
Manipular Columnas
Método Propósito
TableColumnModel
getColumnModel() Obtiene el modelo de columna de la tabla.
(en JTable)
TableColumn getColumn(int)
Enumeration getColumns() Obtiene uno o todos los objetos TableColumn de la tabla.
(en TableColumnModel)
void setMinWidth(int)
void setPreferredWidth(int)
Seleciona la anchura mínima, preferida o máxima de la columna.
void setMaxWidth(int)
(en TableColumn)
int getMinWidth(int)
int getPreferredWidth()
Obtiene la anchura mínima, preferida, máxima o actual de la
int getMaxWidth()
columna.
int getWidth()
(en TableColumn)
Implementar Selección
Método de JTable Propósito
Selecciona los intervalos de selección permitidos en la tabla. Los valores
válidos están definidos
void setSelectionMode(int)
en ListSelectionModel como SINGLE_SELECTION, SINGLE_INTERVA
L_SELECTION, y MULTIPLE_INTERVAL_SELECTION (por defecto).
void
Selecciona u obtiene el modelo usado para controlar las selecciones de
setSelectionModel(ListSelectio
filas.
nModel)
ListSelectionModel
getSelectionModel()
void
setRowSelectionAllowed(boolea
n)
void Selecciona la orientación de selección de la tabla. El argumento booleano
setColumnSelectionAllowed(bool especifica si está permitido el tipo de selección particular. Por defecto,
ean) las selección de filas está permitida, y la de columna y celda no.
void
setCellSelectionEnabled(boolean
)
Esta tabla lista ejemplos que usan JTable y dónde poder encontrarlos.
Dónd
e se
Ejemplos Notas
Descr
ibe
Una tabla básica con un modelo no personalizado. No
Esta
SimpleTableDemo.java incluye código para: especificar anchuras de
página
columna o detectar edición del usuario.
Añade selección sencilla y detección de selección a
SimpleTableDemo. Modificando las
SimpleTable- Esta constantes ALLOW_COLUMN_SELECTION y ALLOW_
SelectionDemo.java página ROW_SELECTION del programa, podemos experimentar
con distintas alternativas para permitir que sólo las filas
sean seleccionadas.
Esta
TableDemo.java Una tabla básica con modelo personalizado.
página
TableEditDemo.java,WholeNumberField. Esta Modifica TableDemo para usar un editor personalizado
java página (una variante de textfiled) para todos los datos Integer.
Modifica TableDemo para usar un editor personalizado
(un combobox) para todos los datos de la columna
Esta 'Sport'. También redimensiona inteligentemente los
TableRenderDemo.java
página tamaños de las columnas y utiliza un intérprete para
mostrar tool-tips para la celdas y la cabecera de la
columna 'Sport'.
Modifica TableEditDemo para tener un intérprete y un
TableDialogEditDemo.java,WholeNumbe Esta
editor de cela que muestre colores y nos permita elegir
rField.java página
uno, utilizando un diálogo selector de colores.
Ordena los datos interponiendo un manipulador de datos
TableSorterDemo.java,TableSorter.jav Esta
entre el modelo de datos y la tabla. Detecta las
a,TableMap.java página
puslaciones del usuario sobre cabeceras de columnas.
Escrib
ir un
Oyent Muestra cómo utilizar todos los modos de selección,
ListSelectionDemo.java e de usando un oyente de list selection que se comparte entre
List una tabla y una lista.
Select
ion