Swing y JFC (Java Foundation Classes)

JTextComponent es la base para los componentes de texto swing, y proporciona estas caracter�sticas personalizables para todos sus descendientes.

  • Un modelo separado, conocido como document, para manejar el contenido del componente.
  • Una vista separada, que se encarga de mostrar el componente en la pantalla. Este tutorial no explica las vistas.
  • Un controlador separado, conocido como un editor kit, que puede leer y escribir texto e implementa capacidades de edici�n con comandos action.
  • Mapas de teclas personalizados.
  • Soporte para repetir/deshacer infinito.
  • Cursores conectables y oyentes de cambios de cursor.

Esta secci�n utiliza la aplicaci�n mostrada abajo para explorar cada una de estas capacidades. La aplicaci�n demo contiene un JTextPane-- una de las subclases de JTextComponent que soporta texto formateado, iconos y componentes embebidos -- para ilustrar las capacidades, heredadas por todas las subclases de JTextComponent. Para m�s informaci�n espec�fica sobre JTextPane pueder ver la p�gina C�mo usar Editor Panes y Text Panes.

El componente de texto superior es el JTextPane personalizado. El componente de texto inferior es un JTextArea, que sirve como un diario que reporta todos los cambios realizados en el contenido del text pane. La l�nea de estado de la parte inferior de la ventana informa sobre la localizaci�n de la selecci�n o de la posici�n del cursor, dependiendo de si hay o no texto seleccionado.

Nota: Es fichero fuente de esta aplicaci�n es TextComponentDemo.java. Tambi�n nevesitar�s LimitedStyledDocument.java.

A trav�s de esta aplicaci�n de ejemplo, aprender�s c�mo usar la capacidades de los componentes de texto y c�mo personalizarlos. Esta secci�n cubre los siguientes textos, que puesen ser aplicados a todas las subclases de JTextComponent.

.�Sobre los Documentos

Al igual que muchos otros componentes Swing, un componente de texto separa su contenido de su vista. El contenido de un componente de este es manejado por su documento, el cual contiene el texto, soporte para edici�n, y notifica a los oyente los cambios en el texto. Un documento es un ejemplar de una clase que implementa el interface Document o su subinterface StyledDocument.

.�Personalizar un Documento

La aplicaci�n de ejemplo mostrada anteriormente tiene un documento persoanlziado LimitedStyledDocument, que limita el n�mero de caracteres que puede contener. LimitedStyledDocument es una subclase de DefaultStyledDocument, el documento por defecto para JTextPane.

Aqu� est� el c�digo del programa de ejemplo que crea un LimitedStyledDocument y lo designa como el documento para le text pane

...donde se declaren las variables miembro...
JTextPane textPane;
static final int MAX_CHARACTERS = 300;
    ...en el constructor del frame...
    //Crea el documento para el �rea de texto
    LimitedStyledDocument lpd = new LimitedStyledDocument(MAX_CHARACTERS);
    ...
    //Crea el text pane y lo configura
    textPane = new JTextPane();
    textPane.setDocument(lpd);
    ...

Para limitar los caracteres permitidos en el docuemnto, LimitedStyledDocument sobreescribe el m�todo insertString de su superclase, que es llamado cada vez que se inserta texto en el documento.

public void insertString(int offs, String str, AttributeSet a)
	           			throws BadLocationException {
    if ((getLength() + str.length()) <= maxCharacters)
        super.insertString(offs, str, a);
    else
        Toolkit.getDefaultToolkit().beep();
}

Adem�s de insertString, los documentos personalizados tambi�n sobreescriben el m�todo remove, que es llamado cada vez que se elimina texto de un documento.

Un uso com�n para un documento personalizado es para crear un campo de texto validado. (un campo cuyo valor es chequeado cada vez que es editado). Para ver dos ejemplos de campos de texto validados pueder ir a Crear un Campo de Texto Validado.

Para m�s informaci�n puedes ver las tablas del API: Clases e Interfaces que Representan Documentos y M�todos �tiles para trabajar con Documento.

.�Escuchar los Cambios de un Documento

Un documento notifica sus cambios a los oyentes interesados. Se utiliza un oyente de Document para reaccionar cuando se inserta o se elimina texto de un documento, o cuando cambia el estilo de alguna parte del texto.

El programa TextComponentDemo usa un oyente de Document para actualizar el diario de cambios siempre que ocurra un cambio en el text pane. Esta l�nea de c�digo registra un ejemplar de MyDocumentListener como oyente del LimitedStyledDocument usado en el ejemplo.

LimitedStyledDocument lpd = new LimitedStyledDocument(MAX_CHARACTERS);
lpd.addDocumentListener(new MyDocumentListener());

Aqu� est� la implementaci�n de MyDocumentListener.

protected class MyDocumentListener implements DocumentListener {
    public void insertUpdate(DocumentEvent e) {
        update(e);
    }
    public void removeUpdate(DocumentEvent e) {
        update(e);
    }
    public void changedUpdate(DocumentEvent e) {
        //Display the type of edit that occurred
        changeLog.append(e.getType().toString() +
                         ": from " + e.getOffset() +
                         " to " + (e.getOffset() + e.getLength() - 1) +
                         newline);
        changeLog.setCaretPosition(changeLog.getDocument().getLength() - 1);
    }
    private void update(DocumentEvent e) {
        //Display the type of edit that occurred and
        //the resulting text length
        changeLog.append(e.getType().toString() +
                         ": text length = " +
                         e.getDocument().getLength() + newline);
        changeLog.setCaretPosition(changeLog.getDocument().getLength() - 1);
    }
} 

El oyente de nuestro ejemplo mueatr el timpo de cambio que ha ocurrido y, si est� afectada por el cambio, la longitud del texto. Para informaci�n general sobre los oyente de Docuement y eventos de Document, puedes ver C�mo escribir un Oyente de Document.

Recuerda que el documento para este text pane limita el n�mero de caracteres permitidos en el decumento. Si intentas a�adir texto hasta exceder el l�mite m�ximo, el documento bloquea el cambio y no se llamar� al m�todo insertUpdate del oyente. Los oyentes de Document s�lo son notificados si el cambio ha ocurrido realmente.

Algunas veces, podr�as estar tentado de cambiar el texto del documento desde dentro de un oyente de Document. Por ejemplo, si tienes un campo de texto s�lo deber�a contener enteros y el usuario introduce alg�n otro tipo de datos, podr�as querer cambiar el texto a 0. Sin embargo, nunca se debe modificar el contenido de un componente de texto desde dentro de un oyente de Document. De hecho, si intentas hacerlo, tu programa se quedar� bloqueado. En su lugar proporciona un documento personalizado y sobreescribe los m�todos insert y remove. Crear un Campo de texto validado te ense�a como hacerlo.

.�Sobre los Kits de Edici�n

Todos los componentes de Texto Swing soportan comandos de edici�n est�ndard como cortar, copiar, pegar y la inserci�n de caracteres. Cada comando de edici�n est� representada e implementado por un objeto action. Esto hace sencillo el asociar un comando con un componente GUI, como un �tem de men� o un bot�n, y construir un GUI alrededor de un componente de texto.

Un componente de texto usa un objeto EditorKit para crear y manejar estas acciones. Adem�s de manejar un conjunto de acciones para un componente de texto, un kit de editor tambi�n sabe leer y escribir documentos en un formato particular.

El paquete text de Swing proporciona estos tres kits de editor.

DefaultEditorKit
Lee y escribe texto sin estilo. Proporciona un conjunto b�sico de comandos de edici�n. Los dem�s kits de editor descienden de este.
StyledEditorKit
Lee y escribe texto con estilo y proporciona un conjunto de acciones m�nimo para texto con estulo. Esta clase es una subclase de DefaultEditorKit y es el kit de editor usado por defecto por JTextPane.
HTMLEditorKit
Lee, escribe y edita HTML. Esta es una subclase de StyledEditorKit.

La mayor�a de los programas no necesitan escribir c�digo que interact�e directamente con los kits de editor porque JTextComponent proporciona el API necesario para invocar directamente a las capacidades del kit. Por ejemplo, JTextComponent proporicona m�todos read y write, que llaman a los m�todos read y write del kit de editor. JTextComponent tambi�n proporcionar un m�todo, getActions, que devuelve todas las acciones soportadas por un componente. Este m�todo obtiene una lista de acciones desde el kit de editor del componente. Sin embargo, las clases del kit de editor proporciona �tiles clases internas y variables de clases que son muy �tiles para crear un GUI alrededor de un componente de texto. Asociar Acciones con �tems de Men� muestra como asociar una acci�n con un �tem de men� y Asociar Acciones con Pulsaciones de Teclas muestra como asociar una acci�n con una pulsaci�n de teclas determinadas. Ambas secciones hacen uso de clases manejables o de variables definidas en los kits de editor est�ndars de Swing.

.�Asociar Acciones con �tems de Men�

Como se mencion� anteriormente, podemos llamar al m�todo getActions sobre cualquier componente para obtener un array que contenga todas las acciones soportadas por dicho componente. Es conveniente cargar el array de acciones en un Hashtable para que nuestro programa pueda recuperar una acci�n por su nombre. Aqu� est� el c�digo de TextComponentDemo que obtiene las acciones del text pane y las carga dentro de un Hashtable.

private void createActionTable(JTextComponent textComponent) {
    actions = new Hashtable();
    Action[] actionsArray = textComponent.getActions();
    for (int i = 0; i < actionsArray.length; i++) {
        Action a = actionsArray[i];
        actions.put(a.getValue(Action.NAME), a);
    }
}    

Y aqu� hay un m�todo de conveniencia para recuperar una acci�n por su nombre desde el hashtable.

private Action getActionByName(String name) {
    return (Action)(actions.get(name));
}

Puedes utilizar ambos m�todos de forma casi literal en tus programas. S�lo tienes que cambiar actions por el nombre de tu hashtable.

Ahora, vemaos c�mo se crea el �tem de me� Cut y como se asocia a la acci�n de eliminar texto de un componente de texto.

protected JMenu createEditMenu() {
    JMenu menu = new JMenu("Edit");
    ...
    menu.add(getActionByName(DefaultEditorKit.cutAction));
    ...

Este c�digo obtiene la acci�n por su nombre usando el m�todo descrito anteriormente y a�ade la acci�n al men�. Esto es todo lo que necesitas hacer. El men� y la acci�n tienen en cuenta todo lo dem�s. Observaras que el nombre de la acci�n viene de DefaultEditorKit. Este kit proporciona acciones para la edici�n b�sica de texto y es la superclase para todos los kits de editor proporcionados por Swing. Por eso sus capacidades est�n disponibles para todos los componentes de texto a menos que se hayan sobreescrito por una personalizaci�n.

Por razones de rendimiento y eficiencia, los componentes de texto comparten acciones. El objeto Action devuelto por getActionByName(DefaultEditorKit.cutAction) es compartido por el JTextArea (no editable) de la parte inferior de la ventana. Esto tiene dos importantes ramificaciones.

  • Generalmente hablando, no se deben modificar los objetos Action obtenidos de los kits de editor. Si lo hacemos, los cambios afectar�n a todos los componentes de texto de nuestro programa.
  • Los objetos Action pueden operar con otros componentes de texto del programa, quiz�s m�s de los esperados. En este ejemplo, incluso aunque no sea editable, el JTextArea comparte las acciones con el JTextPane. Si no queremos compartir, deberemos ejemplarizar el objeto Action nosotros mismos. DefaultEditorKit define varias sublcases de Action muy �tiles.

Configurar el men� Style es similar. Aqu� est� el c�digo que crea y pone el �tem de men� Bold en �l.

protected JMenu createStyleMenu() {
    JMenu menu = new JMenu("Style");
 
    Action action = new StyledEditorKit.BoldAction();
    action.putValue(Action.NAME, "Bold");
    menu.add(action);
    ...

StyledEditorKit proporciona sublcases de Action para implementar comandos de edici�n para texto con estilo. Habr�s notado que en lugar de obtener la acc�n del kit de editor, este c�digo crea un ejemplar de la clase BoldAction. As�i, esta acci�n no ser� compartida por ning�n otro componente de texto, y cambiando su nombre no afectar� a ning�n otro componente de texto.

Adem�s de asociar una acci�n con componente GUI, tambi�n podemos asociar una acci�n con una pulsaci�n de teclas Asociar Acciones con Pulsaciones de Teclas muestra como hacerlo.

Puedes ver la tabla de API relacionada con los Comandos de Edici�n de Texto.

.�Sobre los Mapas de Teclado

Cada componente de texto tiene uno o m�s keymaps-- cada uno de los cuales es un ejemplar de la clase Keymap. Un keymap contiene una colecci�n de parejas nombre-valor donde el nombre es un KeyStroke (pulsaci�n de tecla) y el valor es una Action. Cada parej enlaza el keystroke con la acci�n por lo tanto cada vez que el usuario pulsa la tecla, la acci�n ocurrir�.

Por defecto, un componente de texto tiene un keymap llamado JTextComponent.DEFAULT_KEYMAP. Este keymap contiene enlaces b�sicos est�ndars. Por ejemplo, las teclas de flechas est�n mapeadas para mover el cursor, etc. Se puede modificar o ampliar el kjeymap por defecto de las siguientes formas.

  • A�adiendo un keymao personalizado al componente de texto con del m�todo addKeymap de JTextComponent.
  • A�adiendo enlaces de teclas al keymap por defecto con el m�todo addActionForKeyStroke de Keymap. El Keymap por defecto es compartido entre todos los componentes de texto, utilizalo con precauci�n.
  • Eliminado enlaces de teclas del keymap por defecto con el m�todo removeKeyStrokeBinding de Keymap. El Keymap por defecto es compartido entre todos los componentes de texto, utilizalo con precauci�n.

Cuando se resuelve una pulsaci�n a su acci�n, el componente de texto chequea el keymap en el orden en que fueron a�adidos al componente de texto. As�, el enlace para una pulsaci�n espec�fica en un keymap que hayamos a�adido a un componente de texto sobreescribe cualquier enlace para la misma pulsaci�n en el keymap por defecto.

.�Asociar Acciones con Pulsaciones de Teclas

El text pane de TextComponentDemo a�ade cuatro enlaces de teclas al keymap por defecto.

  • CTRL-B para mover el cursor un caracter hacia atr�s
  • CTRL-F para mover el cursor un caracter hacia adelante
  • CTRL-P para mover el cursor una l�nea hacia arriba
  • CTRL-N para mover el cursor una l�nea hacia abajo.

El siguiente c�digo a�ade el enlace de tecla CTRL-B al keymap por defecto. El c�digo para a�adir las otras tres es similar.

//Obtiene el mapa por defecto actual
Keymap keymap = textPane.addKeymap("MyEmacsBindings",
				   textPane.getKeymap());

//Ctrl-b para ir hacia atr�s un caracter.
Action action = getActionByName(StyledEditorKit.backwardAction);
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_B, Event.CTRL_MASK);
keymap.addActionForKeyStroke(key, action);

Este c�digo primero a�ade el keymap al �rbol de componentes. El m�todo addKeymap crea el keymap por nosotros con el nombre y padre proprocionados en la llamada al m�todo. En este ejemplo, el padre es el keymap por defecto del text pane. Luego, el c�digo obtiene la acci�n de ir hacia atr�s del kit de editor y obtiene un objeto KeyStroke que representa la secuencia de teclas CTRL-B. Finalmente, el c�digo a�ade la pareja acci�n y puslaci�n al keymap, y por lo tanto enlaza la tecla con la acci�n.

Puedes ver el API relacionado en la tabla Enlazar Pulsaciones a Acciones.

.�Implementar Deshacer y Repetir

Nota: La implementaci�n de deshacer/repetir en TextComponentDemo fue copiada directamente del NotePad que viene con Swing. La mayor�a de los programadores tambi�n podr�n copiar esta implementaci�n sin modificaciones.

Implementar Deshacer/repetir tiene dos partes.

.�Parte 1: Recordar Ediciones "Reversibles"

Para soportar deshacer/repetir, un componente de texto debe recordar cada edici�n que ha ocurrido sobre �l, el orden en que ocurren las ediciones en relaci�n a otras, y que hacer para deshacerlas. El programa de ejemplo usa un manejar de deshacer, un ejemplar de la clase UndoManager del paquere undo de Swing, para menjar una lista de ediciones reversibles. El undomanager se crea donde se declaran las variables miembros.

protected UndoManager undo = new UndoManager();

Ahora veamos como el programa encuentra las ediciones reversibles y las a�ade al undomanager.

Un documento noticia a los oyentes interesados si en su contenido ha ocurrido una edici�n reversible. Un paso importante en la implementaci�n de deshacer/repetir es registrar un oynete de 'undoable edit' en el documento del componente de texto. Este c�digo a�ade un ejemplar de MyUndoableEditListener al documento del text pane.

lpd.addUndoableEditListener(new MyUndoableEditListener());

El oyente usado en nuestro ejemplo a�ade la edici�n a la lista del undomanager.

protected class MyUndoableEditListener implements UndoableEditListener {
    public void undoableEditHappened(UndoableEditEvent e) {
        //Recuerda la edici�n y actualiza los men�s
        undo.addEdit(e.getEdit());
        undoAction.update();
        redoAction.update();
    }
}  

Observa que este m�todo actualizad so objetos: undoAction y redoAction. Estos dosn dos objetos acti�n a�adidos a los �tems de men� Undo (Deshacer) y Redo (Repetir) menu items, respectivamente. El siguiente paso es ver como se crean los dos �tems de men� y la implementaci�n de las dos acciones.

Para informaci�n general sobte oyentes y eventos de 'undoable edit' puedes ver: C�mo escribir un oyente de Undoable Edit.

.�Parte 2: Implementar los Comandos Deshacer/Repetir

El primer paso de esta parte de implementaci�n es crear las acciones y ponerlas en el men� Edit.

JMenu menu = new JMenu("Edit");

//Deshacer y repetir son acciones de nuestra propia creacci�n
undoAction = new UndoAction();
menu.add(undoAction);

redoAction = new RedoAction();
menu.add(redoAction);
...

Las acciones deshacer y repetir son implementadas por subclases personalizadas de AbstractAction: UndoAction y RedoAction respectivamente. Estas clases son clases internas de la clases primaria del ejemplo.

Cuando el usuario llama al comando Undo, el m�todo actionPerformed de UndoAction mostrado aqu�, obtiene la llamada.

public void actionPerformed(ActionEvent e) {
    try {
        undo.undo();
    } catch (CannotUndoException ex) {
        System.out.println("Unable to undo: " + ex);
        ex.printStackTrace();
    }
    update();
    redoAction.update();
}

Este m�todo llama al m�todo undo del undomanager y actualiza los �tems de men� para reflejar el nuevo estado de deshacer/repetir.

De forma similar, cuando el usuario llama al comando Redo, el m�todo actionPerformed de RedoAction obtiene la llamada.

public void actionPerformed(ActionEvent e) {
    try {
        undo.redo();
    } catch (CannotRedoException ex) {
        System.out.println("Unable to redo: " + ex);
        ex.printStackTrace();
    }
    update();
    undoAction.update();
}

Este m�todo es similar excepto en que llama al m�todo redo de undomanager.

La mayor�a del c�digo de las clases UndoAction y RedoAction est� dedicada a habilitar o deshabilitar las acciones de forma apropiada al estado actual, y cmabiar los nombres de los �tems de men� para reflejar la edici�n a deshacer o repetir.

.�Escuchar los cambios de cursor o de selecci�n

El programa TextComponentDemo usa un oyente de caret para mostrar la posici�n actual del cursor o, si hay texto selecci�nado la extensi�n de la selecci�n.

El oyente de caret de este ejemplo es tambi�n una etiqueta. Aqu� est� el c�digo que crea la etiqueta, la a�ade a la ventana, y la hace oyente de caret del text pane.

//Crea el �rea de estado
JPanel statusPane = new JPanel(new GridLayout(1, 1));
CaretListenerLabel caretListenerLabel = new CaretListenerLabel(
						"Caret Status");
statusPane.add(caretListenerLabel);
...
textPane.addCaretListener(caretListenerLabel);

Un oyente de caret debe implemenar un m�todo, caretUpdate, que es llamado cada vez que el cursor se mueva o cambie la selecci�n. Aqu� est� la implementaci�n que CaretListenerLabel hace de caretUpdate.

public void caretUpdate(CaretEvent e) {
    //Obtiene la posici�n en el texto
    int dot = e.getDot();
    int mark = e.getMark();
    if (dot == mark) {  // no hay selecci�n
        try {
            Rectangle caretCoords = textPane.modelToView(dot);
            //Convierte las coordenadas
            setText("caret: text position: " + dot +
                    ", view location = [" +
                    caretCoords.x + ", " + caretCoords.y + "]" +
                    newline);
        } catch (BadLocationException ble) {
            setText("caret: text position: " + dot + newline);
        }
     } else if (dot < mark) {
        setText("selection from: " + dot + " to " + mark + newline);
     } else {
        setText("selection from: " + mark + " to " + dot + newline);
     }
}

C�mo puedes ver, este oyenbte actualiza su etiqueta de texto para reflejar el estado actual del cursor o la selecci�n. El oyente obtiene la informaci�n mostrada desde el objeto caret event. Para informaci�n general sobre los oyentes y eventos de cursor puedes ver C�mo escribir un oyente de Caret.

Al igual que los oyenentes de document, un oyente de caret es pasivo. Reacciona a los cambios del cursor o de la selecci�n, pero no cambia el cursor ni la selecci�n. En vez de modidicar el cursor o la selecc�on desde un oyente de caret, deberemos usar un caret personalizado. Para crear un caret peresonalizado, debemos escribir una clase que implemente el interface Caret, luego proporcionar un ejemplar de nuestra clase como argumento a setCaret sobre un componente de texto.

COMPARTE ESTE ARTÍCULO

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