miércoles

JINTERNALFRAME:Con la clase JInternalFrame, se puede mostrar un JFrame - como una ventana dentro de otra ventana. Para crear un frame interno que parezca un diálogo sencillo, se pueden utilizar los métodos showInternalXxxDialog de JOptionPane, como se explicó en Cómo crear Diálogos.

Normalmente, los frames internos se muestran dentro de un JDesktopPane. JDesktopPane es una subclase de JLayeredPane al que se le ha añadido el API para manejar el solapamiento de múltiples frames internos. Generalmente, se pone el panel superior dentro del panel de contenido de un JFrame. Para más información sobre el uso de API que JDesktopPane hereda de JLayeredPane, puedes ver Cómo usar LayeredPane.

Aquí podemos ver una imagen de una aplicación que tiene dos frames internos dentro de un frame normal.

Como se ve en la figura, los frames internos utilizan la decoración de ventana del aspecto y comportamiento Metal. Sin embargo, la ventana que los contiene tiene decoración de aspecto y comportamiento nativo (en este caso, Motif).

JTABBEDPANE

Para continuar nos vamos a basar en el programa TabComDial.java, que es el Organizador de Imágenes que implementa JTabbedPane:

Basta

JTabbedPane tp1 = new JTabbedPane();
Para poder adicionar componentes al JTabbedPane es indispensable crear un JPanel una vez creado, se le adicionan los componentes directamente, para que posteriormente JPanel sea adicionado a JTabbedPane, ponerle un ícono a las pestañas de JTabbedPane es opcional, por ejemplo en la imagen anterior podemos ver el ícono de GRADO 11, en caso de que se desee agregar un ícono se declara dentro de todo el Tabbed Pane de la siguiente forma:
ImageIcon icon = new ImageIcon("icono.jpg");
JPanel p1 = new JPanel();

JTextArea info = new JTextArea(
"En este programa podrá encontrar imágenes" +
" de estudiantes del grado Once, solo señale en la parte superior" +
" el nombre de la persona que desea ver, en caso que quiera" +
" obtener mas información de la persona o del lugar donde fue adquirida la imagen"+
" pulsa el botón que esta en la parte derecha del panel (Información)"
);
p1.add(info, BorderLayout.NORTH);
creditos = new JButton("Créditos");
creditos.addActionListener(this);
creditos.setActionCommand(SHOW);
creditos.setEnabled(true);
p1.add(creditos, BorderLayout.SOUTH);

tp1.addTab("Información", icon, p1, "Información del programa");

martes

Utilizar en Swing un árbol, como los que se despliegan en muchas de las ventanas de los sistemas operativos al uso, es tan simple como escribir:

add( new JTree( new Object[]{ "este","ese","aquel" } ) );

Esto crea un arbolito muy primitivo; sin embargo, el API de Swing para árboles es inmenso, quizá sea uno de los más grandes. En principio parece que se puede hacer cualquier cosa con árboles, pero lo cierto es que si se van a realizar tareas con un cierto grado de complejidad, es necesario un poco de investigación y experimentación, antes de lograr los resultados deseados.

Afortunadamente, como en el término medio está la virtud, en Swing, JavaSoft proporciona los árboles "por defecto", que son los que generalmente necesita el programador la mayoría de las veces, así que no hay demasiadas ocasiones en que haya que entrar en las profundidades de los árboles para que se deba tener un conocimiento exhaustivo del funcionamiento de estos objetos.

El ejemplo java1414.java, utiliza estos árboles predefinidos, para presentar un árbol en un panel. Cuando se pulsa el botón, se añade un nuevo sub-árbol a partir del nodo que está seleccionado; si no hay ninguno, se añade en el raíz.

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.tree.*;

// Esta clase coge un array de Strings, haciendo que el primer elemento
// del array sea un nodo y el resto sean ramas de ese nodo
// Con ello se consiguen las ramas del árbol general cuando se pulsa
// el botón de test
class Rama {
DefaultMutableTreeNode r;
public Rama( String datos[] ) {
r = new DefaultMutableTreeNode( datos[0] );
for( int i=1; i < datos.length; i++ )
r.add( new DefaultMutableTreeNode( datos[i] ) );
}

public DefaultMutableTreeNode node() {
return( r );
}
}

public class java1414 extends JPanel {
String datos[][] = {
{ "Colores","Rojo","Verde","Azul" },
{ "Sabores","Salado","Dulce","Amargo" },
{ "Longitud","Corta","Media","Larga" },
{ "Intensidad","Alta","Media","Baja" },
{ "Temperatura","Alta","Media","Baja" },
{ "Volumen","Alto","Medio","Bajo" },
};
static int i=0;
DefaultMutableTreeNode raiz,rama,seleccion;
JTree arbol;
DefaultTreeModel modelo;

public java1414() {
setLayout( new BorderLayout() );
raiz = new DefaultMutableTreeNode( "raiz" );
arbol = new JTree( raiz );
// Se añade el árbol y se hace sobre un ScrollPane, para
// que se controle automáticamente la longitud del árbol
// cuando está desplegado, de forma que aparecerá una
// barra de desplazamiento para poder visualizarlo en su
// totalidad
add( new JScrollPane( arbol ),BorderLayout.CENTER );
// Se obtiene el modelo del árbol
modelo =(DefaultTreeModel)arbol.getModel();
// Y se añade el botón que va a ir incorporando ramas
// cada vez que se pulse
JButton botonPrueba = new JButton( "Pulsame" );
botonPrueba.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
if( i < datos.length ) {
rama = new Rama( datos[i++] ).node();
// Control de la útlima selección realizada
seleccion = (DefaultMutableTreeNode)
arbol.getLastSelectedPathComponent();
if( seleccion == null )
seleccion = raiz;
// El modelo creará el evento adecuado, y en respuesta
// a él, el árbol se actualizará automáticamente
modelo.insertNodeInto( rama,seleccion,0 );
}
}
} );

// Cambio del color del botón
botonPrueba.setBackground( Color.blue );
botonPrueba.setForeground( Color.white );
// Se crea un panel para contener al botón
JPanel panel = new JPanel();
panel.add( botonPrueba );
add( panel,BorderLayout.SOUTH );
}

public static void main( String args[] ) {
JFrame frame = new JFrame( "Tutorial de Java, Swing" );
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent evt ) {
System.exit( 0 );
}
});
frame.getContentPane().add( new java1414(),BorderLayout.CENTER );
frame.setSize( 200,500 );
frame.setVisible( true );
}
}

La figura siguiente muestra la ventana de ejecución del ejemplo, una vez pulsado el botón varias veces y con algunos de los sub-árboles desplegados.

En la aplicación, la primera clase, Rama, coge un array de String y genera uno de los nodos el árbol, con la primera cadena como raíz y el resto como sus hojas. Posteriormente, se pueden hacer llamadas al método node() para generar la raíz de esta rama.

La clase JTree contiene un array de String de dos dimensiones, a partir del cual salen las ramas, y un contador para moverse a través del array. Los objetos DefaultMutableTreeNode contienen los nodos, pero la representación física sobre la pantalla está controlada por la clase JTree y su modelo asociado, el DefaultTreeModel. Cuando el JTree se incorpora al Frame, es incrustado automáticamente en un JScrollPane, para proporcionar desplazamiento o scroll automático al árbol.

El JTree está controlado a través de un modelo. Cuando se hace un cambio en el modelo, este modelo genera un evento que hace que el JTree realice las actualizaciones necesarias en la representación visible del árbol. En el método init(), en el caso de un applet, o en el constructor, en el caso más general, el modelo que se utiliza es capturado a través del método getModel(). Cuando se pulsa un botón, se crea una nueva rama. Entonces, el Componente seleccionado actual será encontrado (o se convertirá en el raíz, en el caso de que no se encuentre) y el método insertNodeInfo() del modelo hará el trabajo de cambiar el árbol y hacer que se actualice.

En la mayor parte de las ocasiones, un ejemplo como el anterior es capaz de solucionar la mayoría de los problemas de implementación que se propongan. Sin embargo, los árboles tienen el poder de hacer casi cualquier cosa que se pueda imaginar; por ejemplo, en el programa anterior se puede sustituir la palabra "default" por cualquier otra clase para proporcionar un entorno de funcionamiento diferente. Pero hay que andar con cuidado, porque muchas de estas clases tienen un interfaz muy intenso, por lo que puede llevar mucho tiempo el comprender todo el intríngulis de los árboles.

Ya en la discusión de JList, se introdujeron los modelos de datos propios, es decir, una forma determinada de controlas el funcionamiento de uno de los Componentes de Swing. Pero no solamente las listas permiten hacer esto, los árboles también soportan estos modelos, aunque en este caso, debido a la naturaleza jerárquica de los árboles, el modelo es bastante más complejo.

En el ejemplo java1415.java se recrea un árbol que contiene las cartas de una baraja, utilizando un modelo de datos propio para la presentación y manipulación de cada uno de los elementos de las ramas del árbol.

En el código del ejemplo, se incluye la clase anidada que es la que controla cada uno de los elementos.

  public class MiRendererDeArbol extends JLabel implements TreeCellRenderer {
private ImageIcon imgBaraja;
private ImageIcon[] imgPalos;
private ImageIcon[] imgCartas;
private boolean seleccionado;

public MiRendererDeArbol() {
// Cargamos las imgenes de las cartas
imgBaraja = new ImageIcon( "_palos.gif" );
imgPalos = new ImageIcon[4];
imgPalos[0] = new ImageIcon( "_copas.gif" );
imgPalos[1] = new ImageIcon( "_oros.gif" );
imgPalos[2] = new ImageIcon( "_espadas.gif" );
imgPalos[3] = new ImageIcon( "_bastos.gif" );
imgCartas = new ImageIcon[10];
imgCartas[0] = new ImageIcon( "_as.gif" );
imgCartas[1] = new ImageIcon( "_dos.gif" );
imgCartas[2] = new ImageIcon( "_tres.gif" );
imgCartas[3] = new ImageIcon( "_cuatro.gif" );
imgCartas[4] = new ImageIcon( "_cinco.gif" );
imgCartas[5] = new ImageIcon( "_seis.gif" );
imgCartas[6] = new ImageIcon( "_siete.gif" );
imgCartas[7] = new ImageIcon( "_diez.gif" );
imgCartas[8] = new ImageIcon( "_once.gif" );
imgCartas[9] = new ImageIcon( "_doce.gif" );
}

public Component getTreeCellRendererComponent( JTree arbol,
Object valor,boolean seleccionado,boolean expandido,
boolean rama,int fila,boolean conFoco ) {
// Hay que encontrar el nodo en que estamos y coger el
// texto que contiene

DefaultMutableTreeNode nodo = (DefaultMutableTreeNode)valor;
String texto = (String)nodo.getUserObject();
this.seleccionado = seleccionado;
// Se fija el color de fondo en función de que esté o no
// seleccionada la celda del árbol
if( !seleccionado )
setForeground( Color.black );
else
setForeground( Color.white );
// Fijamos el icono que corresponde al texto de la celda, para
// presentar la imagen de la carta que corresponde a esa celda
if( texto.equals( "Palos" ) )
setIcon( imgBaraja );
else if( texto.equals( "Copas" ) )
setIcon( imgPalos[0] );
else if( texto.equals( "Oros" ) )
setIcon( imgPalos[1] );
else if( texto.equals( "Espadas" ) )
setIcon( imgPalos[2] );
else if( texto.equals( "Bastos" ) )
setIcon( imgPalos[3] );
else if( texto.equals( "As" ) )
setIcon( imgCartas[0] );
else if( texto.equals( "Dos" ) )
setIcon( imgCartas[1] );
else if( texto.equals( "Tres" ) )
setIcon( imgCartas[2] );
else if( texto.equals( "Cuatro" ) )
setIcon( imgCartas[3] );
else if( texto.equals( "Cinco" ) )
setIcon( imgCartas[4] );
else if( texto.equals( "Seis" ) )
setIcon( imgCartas[5] );
else if( texto.equals( "Siete" ) )
setIcon( imgCartas[6] );
else if( texto.equals( "Sota" ) )
setIcon( imgCartas[7] );
else if( texto.equals( "Caballo" ) )
setIcon( imgCartas[8] );
else if( texto.equals( "Rey" ) )
setIcon( imgCartas[9] );

// A continuación del icono, ponemos el texto
setText( texto );

return( this);
}

// Sobreescribimos el método paint() para fijar el color de
// fondo. Normalmente, un JLabel puede pintar su propio fondo,
// pero, seguramente debido aparentemente a un bug, o a una
// limitación en el TreeCellRenderer, es necesario recurrir
// al método paint() para hacer esto
public void paint( Graphics g ) {
Color color;
Icon currentI = getIcon();

// Fijamos el colos de fondo
color = seleccionado ? Color.red : Color.white;
g.setColor( color );
// Rellenamos el rectángulo que ocupa el texto sobre la
// celda del árbol
g.fillRect( 0,0,getWidth()-1,getHeight()-1 );

super.paint( g );
}
}

La ejecución del ejemplo, genera una imagen como la que se reproduce a continuación, en donde se ha desplegado una de las ramas del árbol y se ha seleccionado uno de sus elementos.

La clase principal de la aplicación crea un árbol que contiene a cada una de las cartas en uno de los palos de la baraja, y no tiene más misterio. La clase anidada MiRendererDeArbol, es la que implementa el comportamiento que van a tener cada uno de los elementos de las ramas del árbol. Como se puede observar, esta circunstancia permite la inclusión de gráficos o cualquier cambio, como el de la fuente de caracteres, en elementos específicos del árbol. Por ejemplo, puede resultar interesante en una aplicación, el presentar los elementos de un nivel superior en negrilla para resaltar su importancia, o se puede querer añadir una imagen para ser más explicativos. Cualquier cosa de estas, se puede implementar a través de un modelo de datos propio.

En el programa, por ejemplo, los elementos de nivel superior, correspondientes a los palos de la baraja, incluyen la carta que muestra el palo, y a cada una de las cartas individuales, también se les ha añadido el gráfico que las representa. Y en el código, lo único especial es la línea en la cual se le notifica al árbol que dispone de un controlador propio e independiente para la manipulación de las celdas o elementos del árbol.

arbol.setCellRenderer( new MiRendererDeArbol() );

La clase anidada implementa un constructor para la carga de las imágenes, a fin de ahorrar tiempo a la hora de la ejecución. El método getTreeCellRendererComponent(), que debe ser proporcionado a requerimiento de la implementación de la clase TreeCellRenderer utilizada por la clase anidada, es el responsable del dibujo de los elementos del árbol. En este caso, este método determina cuál es la imagen que corresponde al elemento del árbol, basándose en el texto asociado al elemento, y le asigna el icono correspondiente. Además, fija el color de fondo de la celda que contiene al elemento, en función de que esté seleccionado o no.

Con esto último hay un problema. El color de fondo de un elemento del árbol no puede fijarse dentro del método de la clase que implementa el modelo, o por lo menos, si se hace así no tiene efecto alguno. Puede que sea una limitación o un problema de Swing que piensan corregir, pero lo cierto es que esto no ocurre en otros casos, como pueden ser las listas o las tablas. Para evitar el problema, es necesario incluir el método paint() dentro de la clase que implementa el controlador de las celdas.

JTABLE:La JTable controla cómo se presentan los datos, siendo el TableModel quien controla los datos en sí mismos. Para crear una JTable habrá pues que crear un TableModel antes, normalmente. Se puede implementar, para ello, el interfaz TableModel, pero es mucho más simple heredar de la clase ayuda AbstractTableModel. El ejemplo java1416.java muestra esta circunstancia.

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.table.*;
import com.sun.java.swing.event.*;

// El Modelo de la Tabla es el que controla todos los
// datos que se colocan en ella
class ModeloDatos extends AbstractTableModel {
Object datos[][] = {
{"uno","dos","tres","cuatro"},
{"cinco","seis","siete","ocho"},
{"nueve","diez","once","doce"},
};

// Esta clase imprime los datos en la consola cada vez
// que se produce un cambio en cualquiera de las
// casillas de la tabla
class TablaListener implements TableModelListener {
public void tableChanged( TableModelEvent evt ) {
for( int i=0; i < datos.length; i++ ) {
for( int j=0; j < datos[0].length; j++ )
System.out.print( datos[i][j] + " " );
System.out.println();
}
}
}
// Constructor
ModeloDatos() {
addTableModelListener( new TablaListener() );
}
// Devuelve el número de columnas de la tabla
public int getColumnCount() {
return( datos[0].length );
}
// Devuelve el número de filas de la tabla
public int getRowCount() {
return( datos.length );
}
// Devuelve el valor de una determinada casilla de la tabla
// identificada mediante fila y columna
public Object getValueAt( int fila,int col ) {
return( datos[fila][col] );
}
// Cambia el valor que contiene una determinada casilla de
// la tabla
public void setValueAt( Object valor,int fila,int col ) {
datos[fila][col] = valor;
// Indica que se ha cambiado
fireTableDataChanged();
}
// Indica si la casilla identificada por fila y columna es
// editable
public boolean isCellEditable( int fila,int col ) {
return( true );
}
}


public class java1416 extends JPanel {
public java1416() {
setLayout( new BorderLayout() );
JTable tabla = new JTable( new ModeloDatos() );
// La tabla se añade a un ScrollPane para que sea éste el
// que controle automáticamente en tamaño de la tabla,
// presentando una barra de desplazamiento cuando sea
// necesario
JScrollPane panel = new JScrollPane( tabla );
add( panel,BorderLayout.CENTER );
}

public static void main(String args[]) {
JFrame frame = new JFrame( "Tutorial de Java, Swing" );
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent evt ) {
System.exit( 0 );
}
} );
frame.getContentPane().add( new java1416(),BorderLayout.CENTER );
frame.setSize( 200,200 );
frame.setVisible( true );
}
}

En el ejemplo, para no complicar la cosa, DataModel es quien contiene el conjunto de datos, pero también se podrían haber cogido de una base de datos. El constructor añade un receptor de eventos de tipo TableModelListener que va a imprimir el contenido de la tabla cada vez que se produzca un cambio. El resto de los métodos utilizan la notación de los Beans y son utilizados por el objeto JTable cuando se quiere presentar la información del DataModel. AbstractTableModel proporciona por defecto los métodos setValueAt() y isCellEditable() que se utilizan para cambiar los datos, por lo que si se quiere realizar esta operación, no queda más remedio que sobrescribir los dos métodos.

La figura siguiente muestra la Tabla obtenida tras la ejecución del programa en donde se ha editado la casilla correspondiente a la segunda fila y columna B.

Una vez que se tiene un TableModel, ya sólo resta colocarlo en el constructor de JTable. Todos los detalles de presentación, edición y actualización están ocultos al programador. En este ejemplo, se coloca la JTable en un JScrollPane, por lo que es necesario un método especial en JTable.

Las tablas pueden soportar un comportamiento más complejo. En el ejemplo java1417.java, se utiliza un método para cargar un array de ocho columnas por un ciento de filas y, posteriormente, la tabla es configurada para que muestre solamente las líneas verticales y permita la selección simultánea de la fila y columna en que se encuentre la celda marcada.

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.sun.java.swing.*;
import com.sun.java.swing.table.*;

class java1417 extends JPanel {
private JTable tabla;
private JScrollPane panelScroll;
private String titColumna[];
private String datoColumna[][];

public java1417() {
setLayout( new BorderLayout() );
// Creamos las columnas y las cargamos con los datos que van a
// aparecer en la pantalla
CreaColumnas();
CargaDatos();
// Creamos una instancia del componente Swing
tabla = new JTable( datoColumna,titColumna );
// Aquí se configuran algunos de los parámetros que permite
// variar la JTable
tabla.setShowHorizontalLines( false );
tabla.setRowSelectionAllowed( true );
tabla.setColumnSelectionAllowed( true );
// Cambiamos el color de la zona seleccionada (rojo/blanco)
tabla.setSelectionForeground( Color.white );
tabla.setSelectionBackground( Color.red );
// Incorporamos la tabla a un panel que incorpora ya una barra
// de desplazamiento, para que la visibilidad de la tabla sea
// automática
panelScroll = new JScrollPane( tabla );
add( panelScroll, BorderLayout.CENTER );
}


// Creamos las etiquetas que sirven de título a cada una de
// las columnas de la tabla
public void CreaColumnas() {
titColumna = new String[8];

for( int i=0; i < 8; i++ ) {
titColumna[i] = "Col: "+i;
}
}

// Creamos los datos para cada uno de los elementos de la tabla
public void CargaDatos() {
datoColumna = new String[100][8];

for( int iY=0; iY < 100; iY++ ) {
for( int iX=0; iX < 8; iX++ ) {
datoColumna[iY][iX] = "" + iX + "," + iY;
}
}
}


public static void main( String args[] ) {
JFrame ventana = new JFrame( "Tutorial de Java, Swing" );

ventana.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent evt ){
System.exit( 0 );
}
} );
ventana.getContentPane().add( new java1417(),BorderLayout.CENTER );
ventana.setSize( 300,180 );

ventana.setVisible( true );
}
}

La figura siguiente muestra el resultado de la ejecución del ejemplo. Obsérvese que cuano se selecciona un elemento individual de la tabla, aparece una selección en cruz de fila y columna, centrada en la celda que se ha marcado. En este ejemplo los colores de fondo y texto para la región seleccionada se han alterado, que es otra de las facilidades que soporta el componente JTable, el cambio de color para un área de selección.

Aunque el ejemplo contiene un array relativamente grande de datos, la clase JTable no manipula demasiado bien grandes cantidades de información, resultando un rendimiento bastante pobre cuando se sobrepasan los 2000 elementos.

JSCROLLPANE:Cuando un componente ocupa mucho espacio en pantalla o contiene mucha información, un JSCROLLPANE permite asociarle una pequeña vista o ventana deslizable o corrediza que permite solo que se vea una parte de dicho componente.

Un JSCROLLPANE, tambien puede asociarse a otros tipos de panels que contengan muchos componentes.

Algunas sus propiedades mas importantes son:


Autoscrolls(), Background(), Border(), Bounds(), Cursor(), Enabled(), Font(), Foreground(),Insets(), Name(), Opaque(), ToolTipText(), ViewPortBorder(), Visible(), VisibleRect().


Sus escuchadores mas importantes son:


WINDOW: WindowActivated(), WindowClosed(), WindowClosing(), WindowDeactivated(), WindowOpened().

MOUSE: MouseClicked(), MouseDragged(),MouseEntered(), MouseExited(),MouseMoved(), MousePressed(), MouseReleased().

KEY: KeyPressed(), KeyReleased(), KeyTyped().


Programa ejemplo:

import java.lang.*;import java.awt.*;import java.awt.event.*;import javax.swing.*;

public class prog9 {

//declaracion, creacion e inicializacion de componentes, objetos y variables

static JFrame ventana= new JFrame();

static JScrollPane panel1 = new JScrollPane();

static JTextField jt1 = new JTextField(80);

// parte principal de programa

public static void main(String[] args)

{ // area de definicion de propiedades de el objeto

ventana.setTitle("mi programa");

ventana.setDefaultCloseOperation(ventana.EXIT_ON_CLOSE);

//cargando scrollpane panel1 con un componente grandote

panel1.setViewportView(jt1);

// cargando la ventana con el scrollpanel

ventana.getContentPane().add(panel1,BorderLayout.CENTER);

ventana.pack(); ventana.setVisible(true);

//area de asociacion de objeto-eventos

}; // termina main

} // termina clase

De momento el unico componente grandote a usar es el textfield con muchos caracteres, mas adelante se estudiaran componentes mas apropiados para este tipo de panel.

Corrida:

image22.jpg

JPOPUPMENU:La implementación de JPopupMenu resulta un tanto extraña: antes de nada hay que llamar al método enableEvents() y seleccionar los eventos del ratón, en vez de utilizar un receptor de eventos como sería de esperar. Es decir, es posible añadir un receptor de eventos del ratón, pero el evento asociado, MouseEvent, no es capaz de devolver true desde el método isPopupTrigger(); aunque también puede que sea porque el JDK todavía está en fase beta; pero vamos, el comportamiento es ciertamente raro. A pesar de ello, el ejemplo java1412.java, sí que produce el efecto deseado de presentar el menú en la pantalla tal como se muestra:

ante la llegada del evento correspondiente a la activación del menú.

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;

public class java1412 extends JPanel {
JPopupMenu popup = new JPopupMenu();
JTextField txt = new JTextField( 10 );

public java1412() {
add( txt );

ActionListener al = new ActionListener() {
public void actionPerformed( ActionEvent evt ){
txt.setText( ((JMenuItem)evt.getSource()).getText() );
}
};

JMenuItem elemento = new JMenuItem( "Carpanta" );
elemento.addActionListener( al );
popup.add( elemento );
elemento = new JMenuItem( "Rompetechos" );
elemento.addActionListener( al );
popup.add( elemento );
elemento = new JMenuItem( "Otilio" );
elemento.addActionListener( al );
popup.add( elemento );
popup.addSeparator();
elemento = new JMenuItem( "Mortadelo" );
elemento.addActionListener( al );
popup.add( elemento );
enableEvents( AWTEvent.MOUSE_EVENT_MASK );
}

protected void processMouseEvent( MouseEvent evt ){
if( evt.isPopupTrigger() )
popup.show( evt.getComponent(),evt.getX(),evt.getY() );
else
super.processMouseEvent( evt );
}

public static void main( String args[] ) {
JFrame frame = new JFrame( "Tutorial de Java, Swing" );

frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent evt ) {
System.exit( 0 );
}
});

frame.getContentPane().add( new java1412(),BorderLayout.CENTER );
frame.setSize( 200,150 );
frame.setVisible( true );
}
}

El mismo ActionListener es el que se añade a cada JMenuItem, que coge el texto de la etiqueta del menú y la inserta en el JTextField.

JPANEL:El siguiente ejemplo java1418.java, se aprovecha de algunos de los ejemplos anteriores para poder implementar los diferentes paneles. Todos se construyen como descendientes de JPanel, así que en el programa se colocará cada uno de los ejemplos anteriores en su propio panel dentro del JTabbedPane.

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.border.*;

public class java1418 extends JPanel {
static Object objetos[][] = {
{ "AWT-Clon",java1401.class },
{ "Bordes",java1402.class },
{ "Botones",java1404.class },
{ "Grupo de Botones",java1406.class },
{ "Listas y Combo",java1407.class },
{ "Barras",java1413.class },
{ "Arbol",java1414.class },
{ "Tabla",java1416.class },
};

static JPanel creaPanel( Class clase ) {
String titulo = clase.getName();
titulo = titulo.substring( titulo.lastIndexOf('.') + 1 );
JPanel panel = null;
try {
panel = (JPanel)clase.newInstance();
} catch( Exception e ) {
System.out.println( e );
}
panel.setBorder( new TitledBorder( titulo ) );
return( panel );
}

public java1418() {
setLayout( new BorderLayout() );
JTabbedPane pestana = new JTabbedPane();
for( int i=0; i < objetos.length; i++ ) {
pestana.addTab( (String)objetos[i][0],
creaPanel( (Class)objetos[i][1] ) );
}
add( pestana,BorderLayout.CENTER );
pestana.setSelectedIndex( objetos.length/2 );
}

public static void main( String args[] ) {
JFrame frame = new JFrame( "Tutorial de Java, Swing" );
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent evt ) {
System.exit( 0 );
}
} );
frame.getContentPane().add( new java1418(),BorderLayout.CENTER );
frame.setSize( 460,350 );
frame.setVisible( true );
}
}

Se ha utilizado un array para la configuración: el primer elemento es el objeto String que será colocado en la pestaña y el segundo corresponde al objeto de la clase JPanel que será presentado dentro del correspondiente Panel. En el constructor del ejemplo se puede observar que se utilizan los dos métodos importantes del JTabbedPane: addTab() que crea una nueva pestaña y setSelectedIndex() que indica el panel que se va a presentar en primer lugar al arrancar el programa; en este caso se selecciona uno de en medio para mostrar que no es necesario comenzar siempre por el primero. La figura siguiente reproduce esta ventana inicial, en la que aparecen las barras de desplazamiento al igual que se ilustraba uno de los ejemplos anteriores de este capítulo.

En la llamada al método addTab() se le proporciona la cadena que debe colocar en la pestaña y cualquier Componente del AWT. No es necesario que sea un JCompomponent, sino cualquier Componente del AWT, entre los cuales se encuentra, evidentemente, el JComponent, que deriva del Component del AWT. El Componente se presentará sobre el panel y ya no será necesario ningún control posterior, el JTabbedPane se encarga de todo.

El trozo más interesante del código del ejemplo es el correspondiente al método creaPanel() que coge el objeto Class de la clase que se quiere crear y utiliza el método newInstance() para crear uno, moldeándolo a JPanel; desde luego, esto asume que la clase que se quiere añadir debe heredar de JPanel, sino no serviría de nada. Añade un TitledBorder que contiene el nombre de la clase y devuelve el resultado como un JPanel para que sea utilizado en addTab().

  static JPanel creaPanel( Class clase ) {
String titulo = clase.getName();
titulo = titulo.substring( titulo.lastIndexOf('.') + 1 );
JPanel panel = null;
try {
panel = (JPanel)clase.newInstance();
} catch( Exception e ) {
System.out.println( e );
}
panel.setBorder( new TitledBorder( titulo ) );
return( panel );
}