Cómo utilizar Frames Internos

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).


Intenta esto:
  1. Compila y ejecuta la aplicación, Los ficheros fuentes son: InternalFrameDemo.java y MyInternalFrame.java.
  2. Crea nuevos frames internos utilizando el ítem Create en el menú Document.
    Cada frame interno viene 30 pixels por debajo y a la derecha de la posición del frame anterior. Esta funcionalidad se implementa en la clase MyInternalFrame, que es la subclase peronalizada de JInternalFrame.

El siguiente código, tomado de InternalFrameDemo.java, crea el frame principal y los internos del ejemplo anterior.

...//In the constructor of InternalFrameDemo, a JFrame subclass:
    desktop = new JDesktopPane(); //a specialized layered pane
    createFrame(); //Create first window
    setContentPane(desktop);
...
protected void createFrame() {
    MyInternalFrame frame = new MyInternalFrame();
    desktop.add(frame);
    try {
        frame.setSelected(true);
    } catch (java.beans.PropertyVetoException e2) {}
}

...//In the constructor of MyInternalFrame, a JInternalFrame subclass:
static int openFrameCount = 0;
static final int xOffset = 30, yOffset = 30;

public MyInternalFrame() {
    super("Internal Frame #" + (++openFrameCount), 
          true, //resizable
          true, //closable
          true, //maximizable
          true);//iconifiable
    //...Create the GUI and put it in the window...
    //...Then set the window size or call pack...
    ...
    //Set the window's location.
    setLocation(xOffset*openFrameCount, yOffset*openFrameCount);
}

Frames Internos frente a Frames Normales

El código para utilizar frames internos es similar en muchas formas al código para utilizar frames normales Swing. Como los frames internos tienen sus paneles raíz, configurar el GUI para un JInternalFrame es muy similar a configurar el GUI para un JFrame. JInternalFrame también proporciona otro API, como pack, que lo hace similar a JFrame.

Como los frames internos no son ventanas, de alguna forma son diferentes de los frames. Por ejemplo, debemos añadir un frame interno a un contenedor (normalmente un JDesktopPane). Un frame interno no genera eventos window; en su lugar, las acciones del usuario que podrían causar que un frame dispara eventos windows hacen que en un frame interno se disparen eventos "internal frame".

Como los frames internos se han implementado con código independiente de la plataforma, ofrecen algunas características que los frames no pueden ofrecer. Una de esas características es que los frames internos ofrecen más control sobre su estado y capacidades. Programáticamente se puede minimizar o maximizar un frame interno. También se puede especificar el icono que va en la barra de título del frame interno. Incluso podemos especificar si el frame tiene soporte de decoración de ventanas, redimensionado, minimización, cerrado y maximización.

Otra característica es que los frames internos se han diseñado para trabajar con paneles por capas. El API JInternalFrame contiene métodos como moveToFront que funcionan sólo si el contenedor de un frame interno es un layeredpane.

Reglas de utilización de Frames Internos

Si has construido algún programa utilizando JFrame y los otros componentes Swing, entonces ya sabes mucho sobre cómo utilizar frames internos. La siguiente lista sumariza las reglas para la utilización de frames internos.
Se debe seleccionar el tamaño del frame interno.
Si no se selecciona el tamaño del frame interno, tendrá tamaño cero y nunca será visible. Se puede seleccionar el tamaño utilizando uno de estos métodos: setSize, pack o setBounds.
Como regla, se debe seleccionar la posición del frame interno.
Si no se selecciona la localización, empezará en 0,0 (la esquina superior izquierda de su contenedor). Se pueden utilizar los métodos setLocation o setBounds para especificar la esquina superior izquierda del frame interno en relación a su contenedor.
Para añadir componentes a un frame interno, se añaden al panel de contenidos del propio frame interno.
Es exactamente la misma situación que JFrame. Puedes ver Añadir componentes a un Frame para más detalles.
Los diálogos que son frames internos deberían implementarse utilizando JOptionPane o JInternalFrame, no JDialog.
Para crear un diálogo sencillo, podemos utilizar los metodos showInternalXxxDialog de JOptionPane, como se describió en Cómo crear Diálogos. Observa que los diálogos en frames internos no son modales.
Un frame interno se debe añadir a un contenedor
Si no lo añadimos a un contenedor (normalmente un JDesktopPane), el frame interno no aparecerá.
Normalmente no se tiene que llamar a show o setVisible para los frames internos.
Al igual que los botones, los frames internos se muestran automáticamene cuando se añaden a un contenedor visible o cuando su contenedor anteriormente invisible se hace visible.
Los frames internos disparan eventos "internal frame", no eventos "window".
El manejo de eventos "internal frame" es casi idéntico al manejo de eventos "window". Para más información puedes ver Cómo escribir un oyente "Internal Frame".

Nota: Debido a un bug (#4128975), una vez que un frame interno a aparecido y luego ha sido ocultado, el frame interno no aparecerá otra vez. Si queremos mostrarlo de nuevo, tenemos que recrearlo, como lo hace InternalFrameEventDemo.java.

El API de InternalFrame

Las siguientes tablas listan los métodos y constructores más utilizados de JInternalFrame. El API para utilizar frames internos se divide en estas categorías:

Junto con el API listado abajo, JInternalFrame hereda un API útil desde JComponent, Container, y Component. JInternalFrame también proporciona métodos para obtener y seleccionar objetos adicionales en su panel raíz. Para más detalles puedes ver Cómo usar RootPane.

Crear un Frame Interno
Constructor Propósito
JInternalFrame()
JInternalFrame(String)
JInternalFrame(String, boolean)
JInternalFrame(String, boolean, boolean)
JInternalFrame(String, boolean, boolean, boolean)
JInternalFrame(String, boolean, boolean, boolean, boolean)
Crea un ejemplar de JInternalFrame. El primer argumento especificar el título (si existe) a mostrar por el frame interno. El resto de los argumentos especifican si el frame interno debería contener decoración permitiendo al usuario que redimensione, cierre, maximice y minimice el frame interno (por este orden). El valor por defecto para cada argumento booleano es false, lo que significa que la operación no está permitida.
Métodos de la clase JOptionPane:
  • showInternalConfirmDialog
  • showInternalInputDialog
  • showInternalMessageDialog
  • showInternalOptionDialog
Crea un JInternalFrame que simila un diálogo.

Añadir Componentes a un Frame Interno
Método Propósito
void setContentPane(Container)
Container getContentPane()
Selecciona u obtiene el panel de contenido del frame interno, que generalmente contiene todo e GUI del frame interno, con la excepción de la barra de menú y las decoraciones de la ventana.
void setMenuBar(JMenuBar)
JMenuBar getMenuBar()
Selecciona u obtiene la barra de menú del frame interno. Observa que estos nombres de método no contienen "J", al contrario que sus métodos equivalentes de JFrame. En las siguientes versiones de Swing y del JDK 1.2, JInternalFrame añadira setJMenuBar y getJMenuBar, que se deberían utilizar en vez de los métodos existentes.

Especificar el Tamaño y la Posición del Frame Interno
Método Propósito
void pack() Dimensiona el frame interno para que sus componentes tenga sus tamaños preferidos.
void setLocation(Point)
void setLocation(int, int)
Seleciona la posición del frame interno. (Heredada de Component).
void setBounds(Rectangle)
void setBounds(int, int, int, int)
Explicitámente selecciona el tamaño y la localización del frame interno (Heredada de Component).
void setSize(Dimension)
void setSize(int, int)
Explicitámente selecciona el tamaño del frame interno. (Heredada de Component).

Realizar Operaciones de Ventana sobre el Frame Interno
Método Propósito
void setDefaultCloseOperation(int)
int getDefaultCloseOperation()
Selecciona u obtiene lo que hace el frame interno cuando el usuario intenta "cerrar" el frame. El valor por defecto es HIDE_ON_CLOSE. Otros posibles valores son DO_NOTHING_ON_CLOSE y DISPOSE_ON_CLOSE.
void addInternalFrameListener(InternalFrameListener)
void removeInternalFrameListener(InternalFrameListener)
Añade o elimina un oyente de "internal frame" (JInternalFrame es equivalente a un oyente de "window").
void moveToFront()
void moveToBack()
Si el padre del frame interno es un layeredpane, mueve el frame interno adelante o detrás (respectivamente) por sus capas).
void setClosed(boolean)
boolean isClosed()
Selecciona u obtiene si el frame interno está cerrado actualmente.
void setIcon(boolean)
boolean isIcon()
Minimiza o maximiza el frame interno, o determina si está minimizado actualmente.
void setMaximum(boolean)
boolean isMaximum()
Maximiza o restaura el frame interno o determina si está maximizado.
void setSelected(boolean)
boolean isSelected()
Selecciona u obtiene si el frame interno esta actualmente "seleccionado" (activado).

Controlar la decoración y las capacidades de la ventana
Método Propósito
void setFrameIcon(Icon)
Icon getFrameIcon()
Seleciona u obtiene el icono mostrado en la barra de título del frame interno (normalmente en la esquina superior izquierda).
void setClosable(boolean)
boolean isClosable()
Selecciona u obtiene si el usuario puede cerrar el frame interno.
void setIconifiable(boolean)
boolean isIconifiable()
Selecciona u obtiene si el frame interno puede ser minimizado.
void setMaximizable(boolean)
boolean isMaximizable()
Selecciona u obtiene si el usuario puede maximizar el frame interno.
void setResizable(boolean)
boolean isResizable()
Selecciona u obtiene si el frame interno puede ser redimensionado.
void setText(String)
String getText()
Selecciona u obtiene el título de la ventana.
void setWarningString(String)
String getWarningString()
Selecciona u obtiene cualquier string de aviso mostrado en la ventana.

Usar el API de JDesktopPane
Método Propósito
JDesktopPane() Crea un ejemplar de JDesktopPane.
JInternalFrame[] getAllFrames() Devuelve todos los objetos JInternalFrame que contiene el escritorio.
JInternalFrame[] getAllFramesInLayer(int) Devuelve todos los objetos JInternalFrame que contiene el escritorio y que están en la capa especificada. Para más información puedes ver Cómo usar LayeredPane.

Ejemplos que utilizan Frames Internos

Ejemplo Dónde se Describe Notas
MyInternalFrame.java Esta página. Implementa un frame interno que aparece con un desplazamineto con respecto al último frame interno creado.
InternalFrameDemo.java Esta página. Permite crear frames internos (ejemplares de MyInternalFrame) que van dentro del JDesktopPane de la aplicación.
InternalFrameEventDemo.java Cómo escribir un oyente de Internal Frame Demustra la escucha de evento de internalframe. También demuestra el posicionamiento de frames internos dentro del panel de escritorio.
LayeredPaneDemo.java Cómo usar LayeredPane Demuestra cómo poner frames internos en varias capas dentro de un layeredpane.

Ozito